mirror of https://github.com/mikkolehtisalo/iw.git
Initial commit
This commit is contained in:
parent
049f573b6c
commit
054ee5e40a
158
README.md
158
README.md
|
@ -1,2 +1,156 @@
|
|||
iw
|
||||
==
|
||||
Wiki. Kind of.
|
||||
==============
|
||||
|
||||
Handles more like a client application, replication, authenticaton and acl systems. Target group is organization internal usage, writing notes and such.
|
||||
|
||||
Features:
|
||||
* GSSAPI/Kerberos authentication
|
||||
* LDAP backend for user & group information
|
||||
* Three level ACL system for content, with inheritation support
|
||||
* Simple macros (TOC, include children pages)
|
||||
* ExtJS based user interface
|
||||
* Non-destructive versioning data model
|
||||
* Replication to other instances
|
||||
|
||||
[Mandatory screenshot][1]
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
Prerequisities
|
||||
* PostgreSQL
|
||||
* Working Kerberos setup
|
||||
* LDAP server
|
||||
* Golang
|
||||
* Memcached recommended
|
||||
|
||||
Starting up
|
||||
-----------
|
||||
|
||||
This should pull the required packages
|
||||
|
||||
```
|
||||
go get github.com/mikkolehtisalo/iw
|
||||
```
|
||||
|
||||
Change app.secret from app.conf
|
||||
|
||||
```
|
||||
app.secret=OWd2Zg7A5wWomHFWkMNIwGNvS7qCAbGY8NKrSADg50bAaSU4hXemJTslFVV3Ah3Q
|
||||
```
|
||||
|
||||
Database
|
||||
--------
|
||||
|
||||
Create the user, database, and import the schema
|
||||
|
||||
```
|
||||
createuser wiki
|
||||
createdb -U wiki --owner=wiki wiki
|
||||
psql wiki wiki
|
||||
\i db/schema.sql
|
||||
```
|
||||
|
||||
Then review app.conf
|
||||
|
||||
```
|
||||
# DB
|
||||
db.name=wiki
|
||||
db.user=wiki
|
||||
db.password=password
|
||||
```
|
||||
|
||||
LDAP
|
||||
----
|
||||
|
||||
Create bind account, and add photo;binary for users' avatar images.
|
||||
|
||||
Review app.conf, and fill up the LDAP server information, filters and the attributes
|
||||
```
|
||||
# ldap
|
||||
ldap.server=freeipa.localdomain
|
||||
ldap.port=389
|
||||
ldap.user_base=cn=users,cn=accounts,dc=localdomain
|
||||
ldap.user_filter=(&(uid=*)(objectClass=inetUser))
|
||||
ldap.user_uid_attr=uid
|
||||
ldap.user_cn_attr=cn
|
||||
ldap.user_photo_attr=photo;binary
|
||||
ldap.user_group_attr=memberOf
|
||||
ldap.group_filter=(&(cn=*)(objectClass=groupOfNames))
|
||||
ldap.group_cn_attr=cn
|
||||
ldap.group_dn_attr=dn
|
||||
ldap.user=uid=admin,cn=users,cn=accounts,dc=localdomain
|
||||
ldap.passwd=password
|
||||
ldap.group_base=cn=groups,cn=accounts,dc=localdomain
|
||||
ldap.group_regexp=cn=([^,]+)
|
||||
```
|
||||
|
||||
Kerberos
|
||||
--------
|
||||
|
||||
Make sure /etc/krb5.conf is sane, for example
|
||||
|
||||
```
|
||||
[logging]
|
||||
default = FILE:/var/log/krb5libs.log
|
||||
kdc = FILE:/var/log/krb5kdc.log
|
||||
admin_server = FILE:/var/log/kadmind.log
|
||||
|
||||
[libdefaults]
|
||||
default_realm = LOCALDOMAIN
|
||||
dns_lookup_realm = false
|
||||
dns_lookup_kdc = true
|
||||
rdns = false
|
||||
ticket_lifetime = 24h
|
||||
forwardable = yes
|
||||
allow_weak_crypto = false
|
||||
|
||||
[realms]
|
||||
LOCALDOMAIN = {
|
||||
kdc = freeipa.localdomain:88
|
||||
master_kdc = freeipa.localdomain:88
|
||||
admin_server = freeipa.localdomain:749
|
||||
default_domain = localdomain
|
||||
pkinit_anchors = FILE:/etc/ipa/ca.crt
|
||||
}
|
||||
|
||||
[domain_realm]
|
||||
.localdomain = LOCALDOMAIN
|
||||
localdomain = LOCALDOMAIN
|
||||
|
||||
[dbmodules]
|
||||
LOCALDOMAIN = {
|
||||
db_library = ipadb.so
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Create the keytab
|
||||
|
||||
```
|
||||
# Create the principal on kdc
|
||||
kadmin
|
||||
addprinc -randkey HTTP/dev.localdomain@LOCALDOMAIN
|
||||
|
||||
# Add it to the server's /etc/krb5.keytab
|
||||
kadmin
|
||||
ktadd HTTP/dev.localdomain@LOCALDOMAIN
|
||||
```
|
||||
|
||||
Replication
|
||||
-----------
|
||||
|
||||
Replication directory contains client and server. They are very KISS, but working. Build them.
|
||||
|
||||
Accompany with server/client.json file for settings. Create the mentioned CA certificate, and certificates for all servers. Configure valid peers, and other settings. Start the servers and clients.
|
||||
|
||||
All logging goes to syslog.
|
||||
|
||||
The good and the bad
|
||||
====================
|
||||
|
||||
Most parts are pretty solid. Would need test(s/ing) to get rid of some corner case bugs, and proper documentation.
|
||||
|
||||
It's not a ready product, but wouldn't take much to polish it.
|
||||
|
||||
[1]:http://screenshot.org/
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"iw/app/models"
|
||||
"github.com/mikkolehtisalo/revel/acl"
|
||||
)
|
||||
|
||||
type Activities struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
// READ
|
||||
func (a Activities) Read() revel.Result {
|
||||
user := a.Session["username"]
|
||||
revel.TRACE.Printf("Activities Read() user: %+v", user)
|
||||
|
||||
activities := models.GetActivities()
|
||||
filtered := acl.Filter(a.Args, []string{"read", "admin","write"}, activities, false)
|
||||
|
||||
revel.TRACE.Printf("Activities Read() returning: %+v", filtered)
|
||||
return a.RenderJson(filtered)
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package controllers
|
||||
|
||||
import "github.com/revel/revel"
|
||||
|
||||
type App struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
func (c App) Index() revel.Result {
|
||||
return c.Render()
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
. "github.com/mikkolehtisalo/revel/common"
|
||||
"iw/app/models"
|
||||
"encoding/json"
|
||||
"github.com/mikkolehtisalo/revel/acl"
|
||||
"encoding/base64"
|
||||
//"fmt"
|
||||
"time"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
type Attachments struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
// CREATE
|
||||
func (a Attachments) Create(wiki string, attachment string) revel.Result {
|
||||
revel.TRACE.Printf("Attachments Create() wiki: %+v, attachment: %+v", wiki, attachment)
|
||||
|
||||
// Make sure the ids at least look like one
|
||||
if (!IsUUID(wiki)) || (!IsUUID(attachment)) {
|
||||
revel.ERROR.Printf("Garbage attachment %+v/%+v received from %+v", wiki, attachment, a.Session["username"])
|
||||
return a.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the attachment doesn't pre-exist
|
||||
exists_test := models.GetAttachment(wiki, attachment)
|
||||
if exists_test.Wiki_id == wiki && exists_test.Attachment_id == attachment {
|
||||
revel.ERROR.Printf("Attempt to rewrite pre-existing attachment %+v/%+v by user %+v", wiki, attachment, a.Session["username"])
|
||||
return a.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the wiki exists!
|
||||
wiki_exists := models.GetWiki(wiki)
|
||||
if wiki_exists.Wiki_id != wiki {
|
||||
revel.ERROR.Printf("Attempt to add attachment to non-existent wiki: %+v/%+v", wiki, attachment)
|
||||
return a.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Decode attachment from json input
|
||||
var new_att models.Attachment
|
||||
decoder := json.NewDecoder(a.Request.Body)
|
||||
err := decoder.Decode(&new_att)
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Unable to parse attachment %+v/%+v: %+v", wiki, attachment, err)
|
||||
return a.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// ID fields must match!
|
||||
if (new_att.Wiki_id != wiki) || (new_att.Attachment_id != attachment) {
|
||||
revel.ERROR.Printf("Attachment id mismatch %+v/%+v != %+v/%+v from user %+v", wiki, attachment, new_att.Wiki_id, new_att.Attachment_id, a.Session["username"])
|
||||
return a.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Validate fields
|
||||
new_att.Validate(a.Validation)
|
||||
if a.Validation.HasErrors() {
|
||||
revel.ERROR.Printf("Validation errors: %+v", a.Validation.ErrorMap())
|
||||
return a.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the user has rights to create attachment
|
||||
filtered := acl.Filter(a.Args, []string{"admin","write"}, []models.Attachment{new_att}, true)
|
||||
if len(filtered) != 1 {
|
||||
revel.ERROR.Printf("Attempt to create attachment %+v/%+v without access rights: %+v, user: %+v", wiki, attachment, new_att.Filename, a.Session["username"])
|
||||
return a.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
new_att.Create_user = a.Session["username"]
|
||||
new_att.Status = "ACTIVE"
|
||||
new_att.Save(true)
|
||||
|
||||
return a.RenderText("{\"success\":true}")
|
||||
}
|
||||
|
||||
// READ
|
||||
func (a Attachments) Read(wiki string) revel.Result {
|
||||
revel.TRACE.Printf("Attachments Read(): %+v", wiki)
|
||||
|
||||
// Make sure the id looks like one
|
||||
if (!IsUUID(wiki)) {
|
||||
revel.ERROR.Printf("Garbage wiki %+v received from %+v", wiki, a.Session["username"])
|
||||
return a.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
attachments := models.GetAttachments(wiki)
|
||||
// Filter by acls...
|
||||
filtered := acl.Filter(a.Args, []string{"read", "write", "admin"}, attachments, true)
|
||||
|
||||
revel.TRACE.Printf("Attachments Read() returning %+v", filtered)
|
||||
return a.RenderJson(filtered)
|
||||
}
|
||||
|
||||
// Serve direct links!
|
||||
func (a Attachments) Serve(wiki string, attachment string) revel.Result {
|
||||
revel.TRACE.Printf("Attachments Serve(): %+v, attachment: %+v", wiki, attachment)
|
||||
|
||||
// Make sure the ids at least look like one
|
||||
if (!IsUUID(wiki)) || (!IsUUID(attachment)) {
|
||||
revel.ERROR.Printf("Garbage attachment %+v/%+v received from %+v", wiki, attachment, a.Session["username"])
|
||||
return a.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
att := models.GetAttachment(wiki, attachment)
|
||||
|
||||
// Make sure the user has rights to read the attachment
|
||||
filtered := acl.Filter(a.Args, []string{"read", "admin","write"}, []models.Attachment{att}, true)
|
||||
if len(filtered) != 1 {
|
||||
revel.ERROR.Printf("Attempt to read attachment %+v/%+v without access rights: %+v, user: %+v", wiki, attachment, att.Filename, a.Session["username"])
|
||||
return a.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
data, err := base64.StdEncoding.DecodeString(att.Attachment)
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Serve(): Unable to base64 decode attachment! %+v", err)
|
||||
}
|
||||
|
||||
return a.RenderBinary(bytes.NewReader(data), att.Filename, "inline", time.Now())
|
||||
}
|
||||
|
||||
// DELETE
|
||||
func (a Attachments) Delete(wiki string, attachment string) revel.Result {
|
||||
revel.TRACE.Printf("Attachments Delete(): %+v, attachment: %+v", wiki, attachment)
|
||||
|
||||
// Make sure the ids at least look like one
|
||||
if (!IsUUID(wiki)) || (!IsUUID(attachment)) {
|
||||
revel.ERROR.Printf("Garbage attachment %+v/%+v received from %+v", wiki, attachment, a.Session["username"])
|
||||
return a.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
att := models.GetAttachment(wiki, attachment)
|
||||
|
||||
// Make sure the user has rights to delete the attachment
|
||||
filtered := acl.Filter(a.Args, []string{"admin","write"}, []models.Attachment{att}, true)
|
||||
if len(filtered) != 1 {
|
||||
revel.ERROR.Printf("Attempt to delete attachment %+v/%+v without access rights: %+v, user: %+v", wiki, attachment, att.Filename, a.Session["username"])
|
||||
return a.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
att.Create_user = a.Session["username"]
|
||||
att.Status = "DELETED"
|
||||
att.Save(true)
|
||||
|
||||
return a.RenderText("{\"success\":true}")
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
. "github.com/mikkolehtisalo/revel/common"
|
||||
"iw/app/models"
|
||||
"github.com/mikkolehtisalo/revel/acl"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
type ContentFields struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
// READ
|
||||
func (c ContentFields) Read(wiki string, page string) revel.Result {
|
||||
revel.TRACE.Printf("ContentFields Read() wiki: %+v, page: %+v", wiki, page)
|
||||
|
||||
// Make sure the ids at least look like one
|
||||
if (!IsUUID(wiki)) || (!IsUUID(page)) {
|
||||
revel.ERROR.Printf("Garbage contentfield %+v/%+v received from %+v", wiki, page, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Get the contentfield
|
||||
cf := models.GetContent(wiki, page, c.Args)
|
||||
// Check the ACL
|
||||
filtered := acl.Filter(c.Args, []string{"read", "write", "admin"}, []models.ContentField{cf}, true)
|
||||
|
||||
if len(filtered) < 1 {
|
||||
revel.ERROR.Printf("Unable to read content field! wiki: %+v, page: %+v, user: %+v", wiki, page, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
return c.RenderJson(filtered[0])
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
func (c ContentFields) Update(wiki string, page string) revel.Result {
|
||||
|
||||
revel.TRACE.Printf("ContentFields Update() wiki: %+v, page: %+v", wiki, page)
|
||||
|
||||
// Make sure the ids at least look like one
|
||||
if (!IsUUID(wiki)) || (!IsUUID(page)) {
|
||||
revel.ERROR.Printf("Garbage contentfield %+v/%+v received from %+v", wiki, page, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the contentfield pre-exists
|
||||
old_cf := models.GetContent(wiki, page, c.Args)
|
||||
if old_cf.Wiki_id != wiki && old_cf.Contentfield_id != page {
|
||||
revel.ERROR.Printf("Attempt to update non-existing contentfield %+v/%+v by user %+v", wiki, page, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Decode contentfield from json input
|
||||
var new_cf models.ContentField
|
||||
decoder := json.NewDecoder(c.Request.Body)
|
||||
err := decoder.Decode(&new_cf)
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Unable to parse contentfield %+v/%+v: %+v", wiki, page, err)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// ID fields must match!
|
||||
if (new_cf.Wiki_id != wiki) || (new_cf.Contentfield_id != page) {
|
||||
revel.ERROR.Printf("Contentfield id mismatch %+v/%+v != %+v/%+v from user %+v", wiki, page, new_cf.Wiki_id, new_cf.Contentfield_id, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Validate fields
|
||||
new_cf.Validate(c.Validation)
|
||||
if c.Validation.HasErrors() {
|
||||
revel.ERROR.Printf("Validation errors: %+v", c.Validation.ErrorMap())
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the user has rights to the page
|
||||
filtered := acl.Filter(c.Args, []string{"admin","write"}, []models.ContentField{new_cf}, true)
|
||||
if len(filtered) != 1 {
|
||||
revel.ERROR.Printf("Attempt to update contentfield %+v/%+v without access rights! user: %+v", wiki, page, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
new_cf.Create_user = c.Session["username"]
|
||||
new_cf.Status = "ACTIVE"
|
||||
new_cf.Save(true)
|
||||
|
||||
return c.RenderText("{\"success\":true}")
|
||||
}
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"iw/app/models"
|
||||
"github.com/mikkolehtisalo/revel/common"
|
||||
)
|
||||
|
||||
type FavoriteWikis struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
// CREATE
|
||||
func (c FavoriteWikis) Create(wiki string) revel.Result {
|
||||
revel.TRACE.Printf("FavoriteWikis Create(): %+v", wiki)
|
||||
|
||||
// Make sure the id at least looks like one
|
||||
if !common.IsUUID(wiki) {
|
||||
revel.ERROR.Printf("Garbage favorite %+v create received from %+v", wiki, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Already exists?
|
||||
if models.IsFavoriteWiki(wiki, c.Session["username"]) {
|
||||
revel.ERROR.Printf("Wiki %+v already favorite!", wiki)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
fav := models.FavoriteWiki{}
|
||||
fav.Username = c.Session["username"]
|
||||
fav.Wiki_id = wiki
|
||||
fav.Status = "ACTIVE"
|
||||
fav.Save()
|
||||
|
||||
return c.RenderText("{\"success\":true}")
|
||||
}
|
||||
|
||||
// READ
|
||||
func (c FavoriteWikis) Read() revel.Result {
|
||||
revel.TRACE.Printf("FavoriteWikis List()")
|
||||
return c.RenderJson(models.ListFavoriteWikis(c.Session["username"]))
|
||||
}
|
||||
|
||||
|
||||
// DELETE
|
||||
func (c FavoriteWikis) Delete(wiki string) revel.Result {
|
||||
revel.TRACE.Printf("FavoriteWikis Delete(): %+v", wiki)
|
||||
|
||||
// Make sure the id at least looks like one
|
||||
if !common.IsUUID(wiki) {
|
||||
revel.ERROR.Printf("Garbage favorite %+v delete received from %+v", wiki, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Already exists?
|
||||
if !models.IsFavoriteWiki(wiki, c.Session["username"]) {
|
||||
revel.ERROR.Printf("Wiki %+v not favorite!", wiki)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
fav := models.FavoriteWiki{}
|
||||
fav.Username = c.Session["username"]
|
||||
fav.Wiki_id = wiki
|
||||
fav.Status = "DELETED"
|
||||
fav.Save()
|
||||
|
||||
return c.RenderText("{\"success\":true}")
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"iw/app/models"
|
||||
//"encoding/json"
|
||||
//"strings"
|
||||
//"regexp"
|
||||
. "github.com/mikkolehtisalo/revel/common"
|
||||
"github.com/mikkolehtisalo/revel/ldapuserdetails"
|
||||
//"github.com/mikkolehtisalo/revel/acl"
|
||||
)
|
||||
|
||||
type Locks struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
// CREATE
|
||||
func (l Locks) Create(wiki string, target string) revel.Result {
|
||||
revel.TRACE.Printf("Locks Create() wiki: %+v, target: %+v", wiki, target)
|
||||
|
||||
// Make sure the ids at least look like one
|
||||
if (!IsUUID(wiki)) || (!IsUUID(target)) {
|
||||
revel.ERROR.Printf("Garbage lock %+v/%+v received from %+v", wiki, target, l.Session["username"])
|
||||
return l.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the lock doesn't pre-exist
|
||||
exists_test := models.GetLock(wiki, target)
|
||||
if exists_test.Wiki_id == wiki && exists_test.Target_id == target {
|
||||
revel.ERROR.Printf("Attempt to rewrite pre-existing lock %+v/%+v by user %+v", wiki, target, l.Session["username"])
|
||||
return l.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the wiki exists!
|
||||
wiki_exists := models.GetWiki(wiki)
|
||||
if wiki_exists.Wiki_id != wiki {
|
||||
revel.ERROR.Printf("Attempt to add lock to non-existent wiki: %+v/%+v", wiki, target)
|
||||
return l.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// We could check for rights for page, but as obeying lock is non-forcing convenience function, meh
|
||||
|
||||
lock := models.Lock{}
|
||||
lock.Target_id = target
|
||||
lock.Wiki_id = wiki
|
||||
lock.Username = l.Session["username"]
|
||||
dets := l.Args["user_details"].(ldapuserdetails.User_details)
|
||||
lock.Realname = dets.Visiblename
|
||||
lock.Save()
|
||||
|
||||
return l.RenderText("{\"success\":true}")
|
||||
}
|
||||
|
||||
// READ
|
||||
func (l Locks) Read(wiki string, target string) revel.Result {
|
||||
revel.TRACE.Printf("Locks Read() wiki: %+v, target: %+v", wiki, target)
|
||||
|
||||
// Make sure the ids at least look like one
|
||||
if (!IsUUID(wiki)) || (!IsUUID(target)) {
|
||||
revel.ERROR.Printf("Garbage lock %+v/%+v received from %+v", wiki, target, l.Session["username"])
|
||||
return l.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Get the lock
|
||||
lock := models.GetLock(wiki, target)
|
||||
|
||||
return l.RenderJson(lock)
|
||||
}
|
||||
|
||||
// DELETE
|
||||
func (l Locks) Delete(wiki string, target string) revel.Result {
|
||||
revel.TRACE.Printf("Locks Delete() wiki: %+v, target: %+v", wiki, target)
|
||||
|
||||
// Make sure the ids at least look like one
|
||||
if (!IsUUID(wiki)) || (!IsUUID(target)) {
|
||||
revel.ERROR.Printf("Garbage lock %+v/%+v received from %+v", wiki, target, l.Session["username"])
|
||||
return l.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Get the lock
|
||||
lock := models.GetLock(wiki, target)
|
||||
lock.Delete()
|
||||
|
||||
return l.RenderText("{\"success\":true}")
|
||||
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"iw/app/models"
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"regexp"
|
||||
. "github.com/mikkolehtisalo/revel/common"
|
||||
"github.com/mikkolehtisalo/revel/acl"
|
||||
)
|
||||
|
||||
type Pages struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
// CREATE
|
||||
func (c Pages) Create(wiki string, page string) revel.Result {
|
||||
revel.TRACE.Printf("Pages Create() wiki: %+v, page: %+v", wiki, page)
|
||||
|
||||
// Make sure the ids at least look like one
|
||||
if (!IsUUID(wiki)) || (!IsUUID(page)) {
|
||||
revel.ERROR.Printf("Garbage page %+v/%+v received from %+v", wiki, page, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the page doesn't pre-exist
|
||||
exists_test := models.GetPage(wiki, page)
|
||||
if exists_test.Wiki_id == wiki && exists_test.Page_id == page {
|
||||
revel.ERROR.Printf("Attempt to rewrite pre-existing page %+v/%+v by user %+v", wiki, page, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the wiki exists!
|
||||
wiki_exists := models.GetWiki(wiki)
|
||||
if wiki_exists.Wiki_id != wiki {
|
||||
revel.ERROR.Printf("Attempt to add page to non-existent wiki: %+v/%+v", wiki, page)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Decode page from json input
|
||||
var new_page models.Page
|
||||
decoder := json.NewDecoder(c.Request.Body)
|
||||
err := decoder.Decode(&new_page)
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Unable to parse page %+v/%+v: %+v", wiki, page, err)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// ID fields must match!
|
||||
if (new_page.Wiki_id != wiki) || (new_page.Page_id != page) {
|
||||
revel.ERROR.Printf("Page id mismatch %+v/%+v != %+v/%+v from user %+v", wiki, page, new_page.Wiki_id, new_page.Page_id, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Validate fields
|
||||
new_page.Validate(c.Validation)
|
||||
if c.Validation.HasErrors() {
|
||||
revel.ERROR.Printf("Validation errors: %+v", c.Validation.ErrorMap())
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the user has rights to create page here
|
||||
filtered := acl.Filter(c.Args, []string{"admin","write"}, []models.Page{new_page}, true)
|
||||
if len(filtered) != 1 {
|
||||
revel.ERROR.Printf("Attempt to create page %+v/%+v without access rights: %+v, user: %+v", wiki, page, new_page.Title, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
new_page.Create_user = c.Session["username"]
|
||||
new_page.Status = "ACTIVE"
|
||||
new_page.Save(true)
|
||||
|
||||
// Create corresponding contentfield!
|
||||
cf := models.ContentField{}
|
||||
cf.Contentfield_id = new_page.Page_id
|
||||
cf.Wiki_id = new_page.Wiki_id
|
||||
cf.Content = "<p>Add new content here!</p>"
|
||||
cf.Status = "ACTIVE"
|
||||
cf.Create_user = c.Session["username"]
|
||||
cf.Save(false)
|
||||
|
||||
return c.RenderText("{\"success\":true}")
|
||||
}
|
||||
|
||||
// READ
|
||||
func (c Pages) Read() revel.Result {
|
||||
node := c.Params.Values.Get("node")
|
||||
revel.TRACE.Printf("Pages Read() node: %+v", node)
|
||||
|
||||
// Check that the input parameter is at least roughly valid
|
||||
re := regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})*")
|
||||
if !re.MatchString(node) {
|
||||
revel.TRACE.Printf("Pages List() invalid node: %+v", node)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// This won't blow up thanks to previous
|
||||
wiki := strings.Split(node, "/")[0]
|
||||
page := strings.Split(node, "/")[1]
|
||||
|
||||
// Get the list of pages
|
||||
pages := models.ListPages(wiki, page)
|
||||
// Filter using the ACL system
|
||||
filtered := acl.Filter(c.Args, []string{"read","write","admin"}, pages, true)
|
||||
revel.TRACE.Printf("Returning: %+v", filtered)
|
||||
|
||||
return c.RenderJson(filtered)
|
||||
}
|
||||
|
||||
// UPDATE
|
||||
// Not DRY, but this logic might still change: revise later...
|
||||
func (c Pages) Update(wiki string, page string) revel.Result {
|
||||
revel.TRACE.Printf("Pages Update() wiki: %+v, page: %+v", wiki, page)
|
||||
|
||||
// Make sure the ids at least look like one
|
||||
if (!IsUUID(wiki)) || (!IsUUID(page)) {
|
||||
revel.ERROR.Printf("Garbage page %+v/%+v received from %+v", wiki, page, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the page pre-exists
|
||||
// Also the wiki probably exists if the page exists
|
||||
old_page := models.GetPage(wiki, page)
|
||||
if old_page.Wiki_id != wiki && old_page.Page_id != page {
|
||||
revel.ERROR.Printf("Attempt to update non-existing page %+v/%+v by user %+v", wiki, page, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Decode page from json input
|
||||
var new_page models.Page
|
||||
decoder := json.NewDecoder(c.Request.Body)
|
||||
err := decoder.Decode(&new_page)
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Unable to parse page %+v/%+v: %+v", wiki, page, err)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// ID fields must match!
|
||||
if (new_page.Wiki_id != wiki) || (new_page.Page_id != page) {
|
||||
revel.ERROR.Printf("Page id mismatch %+v/%+v != %+v/%+v from user %+v", wiki, page, new_page.Wiki_id, new_page.Page_id, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Validate fields
|
||||
new_page.Validate(c.Validation)
|
||||
if c.Validation.HasErrors() {
|
||||
revel.ERROR.Printf("Validation errors: %+v", c.Validation.ErrorMap())
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the user has rights to the page
|
||||
filtered := acl.Filter(c.Args, []string{"admin","write"}, []models.Page{new_page}, true)
|
||||
if len(filtered) != 1 {
|
||||
revel.ERROR.Printf("Attempt to create page %+v/%+v without access rights: %+v, user: %+v", wiki, page, new_page.Title, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
new_page.Create_user = c.Session["username"]
|
||||
new_page.Status = "ACTIVE"
|
||||
new_page.Save(true)
|
||||
|
||||
return c.RenderText("{\"success\":true}")
|
||||
}
|
||||
|
||||
|
||||
// DELETE
|
||||
func (c Pages) Delete(wiki string, page string) revel.Result {
|
||||
revel.TRACE.Printf("Pages Delete() wiki: %+v, page: %+v", wiki, page)
|
||||
|
||||
// If the parameters don't seem valid, bail out
|
||||
if (!IsUUID(wiki)) || (!IsUUID(page)) {
|
||||
revel.ERROR.Printf("Pages Delete() invalid IDs! wiki: %+v, page: %+v", wiki, page)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
p := models.GetPage(wiki, page)
|
||||
if p.Wiki_id == "" {
|
||||
revel.ERROR.Printf("Pages Delete() Attempt to delete non-existing page! user: %+v wiki %+v page %+v", c.Session["username"], wiki, page)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Write or admin is enough to delete
|
||||
filtered := acl.Filter(c.Args, []string{"write", "admin"}, []models.Page{p}, true)
|
||||
if len(filtered) < 1 {
|
||||
revel.ERROR.Printf("Pages Delete() insufficient rights! user: %+v wiki: %+v, page: %+v", c.Session["username"], wiki, page)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Also handles children and contentfields
|
||||
models.DeletePage(wiki, page, c.Session["username"], true)
|
||||
|
||||
return c.RenderText("{\"success\":true}")
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"bytes"
|
||||
"github.com/mikkolehtisalo/revel/ldapuserdetails"
|
||||
"time"
|
||||
"regexp"
|
||||
"github.com/revel/revel/cache"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type UserAvatars struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
func (u UserAvatars) Read(user string) revel.Result {
|
||||
revel.TRACE.Printf("UserAvatars Read() user:%+v", user)
|
||||
var avatar []byte
|
||||
|
||||
err := cache.Get(fmt.Sprintf("avatar:%s", user), &avatar)
|
||||
|
||||
if err != nil {
|
||||
// Not in cache, generate
|
||||
re := regexp.MustCompile("^(\\w*)\\.(jpeg|jpg|png)")
|
||||
|
||||
if !re.MatchString(user) {
|
||||
revel.TRACE.Printf("UserAvatars Read() invalid user: %+v", user)
|
||||
return u.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
username := re.FindStringSubmatch(user)[1]
|
||||
dets := ldapuserdetails.Get_user_details(username)
|
||||
avatar = dets.Photo
|
||||
go cache.Set(fmt.Sprintf("avatar:%s", user), avatar, cache.DEFAULT)
|
||||
}
|
||||
|
||||
return u.RenderBinary(bytes.NewReader(avatar), user, "inline", time.Now())
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package controllers
|
||||
|
||||
import "github.com/revel/revel"
|
||||
import "iw/app/models"
|
||||
//import "fmt"
|
||||
|
||||
type UserGroupSearch struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
func (c UserGroupSearch) List() revel.Result {
|
||||
query := c.Params.Query["query"][0]
|
||||
return c.RenderJson(models.ListUserGroupSearchItems(query))
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"iw/app/models"
|
||||
"encoding/json"
|
||||
. "github.com/mikkolehtisalo/revel/common"
|
||||
"github.com/mikkolehtisalo/revel/acl"
|
||||
)
|
||||
|
||||
type Wikis struct {
|
||||
*revel.Controller
|
||||
}
|
||||
|
||||
// CREATE
|
||||
func (c Wikis) Create(wiki string) revel.Result {
|
||||
revel.TRACE.Printf("Wikis Create(): %+v", wiki)
|
||||
|
||||
// Make sure the id at least looks like one
|
||||
if !IsUUID(wiki) {
|
||||
revel.ERROR.Printf("Garbage wiki %+v received from %+v", wiki, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the wiki doesn't pre-exist
|
||||
exists_test := models.GetWiki(wiki)
|
||||
if exists_test.Wiki_id == wiki {
|
||||
revel.ERROR.Printf("Attempt to rewrite pre-existing wiki %+v by user %+v", wiki, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Decode wiki from input json
|
||||
var new_wiki models.Wiki
|
||||
decoder := json.NewDecoder(c.Request.Body)
|
||||
err := decoder.Decode(&new_wiki)
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Unable to parse wiki %+v: %+v", wiki, err)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// ID fields must match!
|
||||
if new_wiki.Wiki_id != wiki {
|
||||
revel.ERROR.Printf("Wiki id mismatch %+v != %+v", new_wiki.Wiki_id, wiki)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Validate fields
|
||||
new_wiki.Validate(c.Validation)
|
||||
if c.Validation.HasErrors() {
|
||||
revel.ERROR.Printf("Validation errors parsing wiki %+v: %+v", wiki, c.Validation.ErrorMap())
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make user the author has admin access right by default
|
||||
AddUserToACLList(c.Session["username"], &new_wiki.Adminacl)
|
||||
|
||||
// Save the wiki
|
||||
new_wiki.Create_user = c.Session["username"]
|
||||
new_wiki.Status = "ACTIVE"
|
||||
new_wiki.Save(true)
|
||||
|
||||
revel.INFO.Printf("User %+v created wiki %+v: %+v", c.Session["username"], new_wiki.Wiki_id, new_wiki.Title )
|
||||
return c.RenderText("{\"success\":true}")
|
||||
}
|
||||
|
||||
// READ
|
||||
func (c Wikis) Read() revel.Result {
|
||||
revel.TRACE.Printf("Wikis Read()")
|
||||
|
||||
wikis := models.ListWikis()
|
||||
filtered := acl.Filter(c.Args, []string{"read","write","admin"}, wikis, false)
|
||||
|
||||
revel.TRACE.Printf("Wikis Read() returning: %+v", filtered)
|
||||
return c.RenderJson(filtered)
|
||||
}
|
||||
|
||||
|
||||
// UPDATE
|
||||
func (c Wikis) Update(wiki string) revel.Result {
|
||||
revel.TRACE.Printf("Wikis Update(): %s", wiki)
|
||||
|
||||
// Make sure the id at least looks like one
|
||||
if !IsUUID(wiki) {
|
||||
revel.ERROR.Printf("Garbage wiki %+v received from %+v", wiki, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the wiki exists
|
||||
exists_test := models.GetWiki(wiki)
|
||||
if exists_test.Wiki_id != wiki {
|
||||
revel.ERROR.Printf("Attempt to update non-existing wiki %+v by user %+v", wiki, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Decode the wiki from input
|
||||
var new_wiki models.Wiki
|
||||
decoder := json.NewDecoder(c.Request.Body)
|
||||
err := decoder.Decode(&new_wiki)
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Unable to parse wiki %+v: %+v", wiki, err)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// ID fields must match!
|
||||
if new_wiki.Wiki_id != wiki {
|
||||
revel.ERROR.Printf("Wiki id mismatch %+v != %+v", new_wiki.Wiki_id, wiki)
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Validate fields
|
||||
new_wiki.Validate(c.Validation)
|
||||
if c.Validation.HasErrors() {
|
||||
revel.ERROR.Printf("Validation errors: %+v", c.Validation.ErrorMap())
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
// Make sure the user has rights to modify the wiki
|
||||
filtered := acl.Filter(c.Args, []string{"admin","write"}, []models.Wiki{exists_test}, false)
|
||||
if len(filtered) != 1 {
|
||||
revel.ERROR.Printf("Attempt to update wiki without access rights: %+v: %+v, user: %+v", exists_test.Wiki_id, exists_test.Title, c.Session["username"])
|
||||
return c.RenderText("{\"success\":false}")
|
||||
}
|
||||
|
||||
new_wiki.Status = "ACTIVE"
|
||||
new_wiki.Create_user = c.Session["username"]
|
||||
new_wiki.Save(true)
|
||||
|
||||
revel.INFO.Printf("User %+v updated wiki %+v: %+v", c.Session["username"], new_wiki.Wiki_id, new_wiki.Title )
|
||||
|
||||
return c.RenderText("{\"success\":true}")
|
||||
}
|
||||
|
||||
// DELETE
|
||||
func (c Wikis) Delete(wiki string) revel.Result {
|
||||
revel.TRACE.Printf("Wikis Delete(): %s", wiki)
|
||||
wi := models.GetWiki(wiki)
|
||||
filtered := acl.Filter(c.Args, []string{"admin"}, []models.Wiki{wi}, false)
|
||||
|
||||
// Delete everything that survived filtering
|
||||
for _, w := range filtered {
|
||||
// Will also do other house cleaning
|
||||
w.(models.Wiki).Delete(c.Session["username"])
|
||||
revel.INFO.Printf("User %+v deleted wiki %+v: %+v", c.Session["username"], w.(models.Wiki).Wiki_id, w.(models.Wiki).Title)
|
||||
}
|
||||
|
||||
return c.RenderText("{\"success\":true}")
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package app
|
||||
|
||||
import "github.com/revel/revel"
|
||||
import "github.com/mikkolehtisalo/revel/cachesession"
|
||||
import "github.com/mikkolehtisalo/revel/gssserver"
|
||||
import "github.com/mikkolehtisalo/revel/ldapuserdetails"
|
||||
import "github.com/cbonello/revel-csrf"
|
||||
|
||||
func init() {
|
||||
// Filters is the default set of global filters.
|
||||
revel.Filters = []revel.Filter{
|
||||
revel.PanicFilter, // Recover from panics and display an error page instead.
|
||||
revel.RouterFilter, // Use the routing table to select the right Action
|
||||
revel.FilterConfiguringFilter, // A hook for adding or removing per-Action filters.
|
||||
revel.ParamsFilter, // Parse parameters into Controller.Params.
|
||||
cachesession.CacheSessionFilter, // Use cache based session implementation.
|
||||
gssserver.GSSServerFilter, // GSSAPI authentication
|
||||
ldapuserdetails.UserDetailsLoadFilter, // Load user details from LDAP
|
||||
revel.FlashFilter, // Restore and write the flash cookie.
|
||||
csrf.CSRFFilter, // CSRF prevention.
|
||||
revel.ValidationFilter, // Restore kept validation errors and save new ones from cookie.
|
||||
revel.I18nFilter, // Resolve the requested language
|
||||
HeaderFilter, // Add some security based headers
|
||||
revel.InterceptorFilter, // Run interceptors around the action.
|
||||
revel.CompressFilter, // Compress the result.
|
||||
revel.ActionInvoker, // Invoke the action.
|
||||
}
|
||||
|
||||
// register startup functions with OnAppStart
|
||||
// ( order dependent )
|
||||
// revel.OnAppStart(InitDB())
|
||||
// revel.OnAppStart(FillCache())
|
||||
}
|
||||
|
||||
// TODO turn this into revel.HeaderFilter
|
||||
// should probably also have a filter for CSRF
|
||||
// not sure if it can go in the same filter or not
|
||||
var HeaderFilter = func(c *revel.Controller, fc []revel.Filter) {
|
||||
// Add some common security headers
|
||||
c.Response.Out.Header().Add("X-Frame-Options", "SAMEORIGIN")
|
||||
c.Response.Out.Header().Add("X-XSS-Protection", "1; mode=block")
|
||||
c.Response.Out.Header().Add("X-Content-Type-Options", "nosniff")
|
||||
|
||||
fc[0](c, fc[1:]) // Execute the next filter stage.
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/revel/revel"
|
||||
"github.com/twinj/uuid"
|
||||
"github.com/mikkolehtisalo/revel/ldapuserdetails"
|
||||
"github.com/mikkolehtisalo/revel/acl"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Activity struct {
|
||||
Activity_id string
|
||||
Timestamp time.Time
|
||||
User_id string
|
||||
User_name string
|
||||
Activity_type string
|
||||
Target_type string
|
||||
Target_title string
|
||||
Target_id string
|
||||
MatchedPermissions []string
|
||||
// For the "flattened" acls...
|
||||
Readacl string
|
||||
Writeacl string
|
||||
Adminacl string
|
||||
}
|
||||
|
||||
// Oldest: 1 month
|
||||
func GetActivities() []Activity {
|
||||
revel.TRACE.Printf("GetActivities()")
|
||||
activities := []Activity{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&activities, "select * from activities where timestamp > now() - interval '1 month' order by timestamp desc")
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("GetActivities() err: %+v", err)
|
||||
}
|
||||
|
||||
revel.TRACE.Printf("GetActivities() returning: %+v", activities)
|
||||
return activities
|
||||
}
|
||||
|
||||
func GetActivity(activity_id string) Activity {
|
||||
revel.TRACE.Printf("GetActivity() id: %+v", activity_id)
|
||||
activities := []Activity{}
|
||||
activity := Activity{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&activities, "select * from activities where activity_id=uuid_in($1)", activity_id)
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Unable to get activity %+v: %+v", activity_id, err)
|
||||
}
|
||||
|
||||
if len(activities) > 0 {
|
||||
activity = activities[0]
|
||||
}
|
||||
|
||||
revel.TRACE.Printf("GetActivity() returning: %+v", activity)
|
||||
|
||||
return activity
|
||||
}
|
||||
|
||||
// Is there a similar activity within one minute range?
|
||||
func ActivityExists(a Activity) bool {
|
||||
exists := false
|
||||
|
||||
activities := []Activity{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&activities, "select * from activities where $1 - timestamp < interval '1 minute' and user_id=$2 and activity_type=$3 and target_type=$4 and target_id=$5",
|
||||
a.Timestamp, a.User_id, a.Activity_type, a.Target_type, a.Target_id)
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("ActivityExists() err: %+v", err)
|
||||
}
|
||||
|
||||
if len(activities) > 0 {
|
||||
exists = true
|
||||
}
|
||||
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
// Flatten the inherited ACL structure, for performance reasons
|
||||
// If the ACLs of the parent items are edited, the ACLs of activity will not change..
|
||||
func update_acl(a *Activity) {
|
||||
filterable := acl.Get_filterable([]Activity{*a})
|
||||
acls := acl.GetACLEntry("activity:" + a.Activity_id, filterable[0], true)
|
||||
|
||||
// Remove the duplicates from previous (it will generate them) and flatten the result
|
||||
var read, write, admin []string
|
||||
for _, item := range acls.ACLs {
|
||||
if item.Permission == "read" && item.Principal != "" {
|
||||
read = append(read, item.Principal)
|
||||
}
|
||||
if item.Permission == "write" && item.Principal != "" {
|
||||
write = append(write, item.Principal)
|
||||
}
|
||||
if item.Permission == "admin" && item.Principal != "" {
|
||||
admin = append(admin, item.Principal)
|
||||
}
|
||||
}
|
||||
|
||||
a.Readacl = strings.Join(read, ",")
|
||||
a.Writeacl = strings.Join(write, ",")
|
||||
a.Adminacl = strings.Join(admin, ",")
|
||||
}
|
||||
|
||||
// Flattens the ACL before saving
|
||||
func (a *Activity) Save() {
|
||||
revel.TRACE.Printf("Activity Save(): %+v", a)
|
||||
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
update_acl(a)
|
||||
|
||||
_, err := db.Exec("insert into activities(activity_id, timestamp, user_id, user_name, activity_type, target_type, target_title, target_id, readacl, writeacl, adminacl) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)",
|
||||
a.Activity_id, a.Timestamp, a.User_id, a.User_name, a.Activity_type, a.Target_type, a.Target_title, a.Target_id, a.Readacl, a.Writeacl, a.Adminacl)
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Activity Save(): error %+v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func SaveActivity(target interface{}) {
|
||||
revel.TRACE.Printf("target: %+v", target)
|
||||
a := Activity{}
|
||||
a.Activity_id = uuid.NewV4().String()
|
||||
|
||||
switch target.(type) {
|
||||
case *Wiki:
|
||||
w := target.(*Wiki)
|
||||
a.Timestamp = w.Modified
|
||||
a.User_id = w.Create_user
|
||||
dets := ldapuserdetails.Get_user_details(w.Create_user)
|
||||
a.User_name = dets.Visiblename
|
||||
a.Activity_type = w.Status
|
||||
a.Target_type = "WIKI"
|
||||
a.Target_title = w.Title
|
||||
a.Target_id = w.Wiki_id
|
||||
if !ActivityExists(a) {
|
||||
a.Save()
|
||||
}
|
||||
case *Page:
|
||||
p := target.(*Page)
|
||||
a.Timestamp = p.Modified
|
||||
a.User_id = p.Create_user
|
||||
dets := ldapuserdetails.Get_user_details(p.Create_user)
|
||||
a.User_name = dets.Visiblename
|
||||
a.Activity_type = p.Status
|
||||
a.Target_type = "PAGE"
|
||||
a.Target_title = p.Title
|
||||
|
||||
// We have to save the parent node for treeStore, or "" if it's on first level....
|
||||
parent := ""
|
||||
if p.Depth > 0 {
|
||||
split := strings.Split(p.Path, "/")
|
||||
parent = split[len(split)-2]
|
||||
}
|
||||
a.Target_id = p.Wiki_id + "/" +parent + "/" + p.Page_id
|
||||
|
||||
if !ActivityExists(a) {
|
||||
a.Save()
|
||||
}
|
||||
case *Attachment:
|
||||
x := target.(*Attachment)
|
||||
a.Timestamp = x.Modified
|
||||
a.User_id = x.Create_user
|
||||
dets := ldapuserdetails.Get_user_details(x.Create_user)
|
||||
a.User_name = dets.Visiblename
|
||||
a.Activity_type = x.Status
|
||||
a.Target_type = "ATTACHMENT"
|
||||
a.Target_title = x.Filename
|
||||
a.Target_id = x.Wiki_id + "/" + x.Attachment_id
|
||||
if !ActivityExists(a) {
|
||||
a.Save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ACL stuff
|
||||
|
||||
// Never inherit the parent!
|
||||
func (a Activity) BuildACLInheritation() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// Activity + activity_id
|
||||
func (a Activity) BuildACLReference() string {
|
||||
return "activity:" + a.Activity_id
|
||||
}
|
||||
|
||||
// No parent..
|
||||
func (a Activity) BuildACLParent() string {
|
||||
return strings.ToLower(a.Target_type) + ":" + a.Target_id
|
||||
}
|
||||
|
||||
// Set the matched permissions to a variable
|
||||
func (a Activity) SetMatched(permissions []string) interface{} {
|
||||
a.MatchedPermissions = permissions
|
||||
return a
|
||||
}
|
||||
|
||||
func (a Activity) BuildACLEntry(reference string) acl.ACLEntry {
|
||||
revel.TRACE.Printf("BuildACLEntry() %+v", reference)
|
||||
var entry acl.ACLEntry
|
||||
|
||||
if reference != ("activity:"+a.Activity_id) {
|
||||
|
||||
if strings.Index(reference, "page") == 0 {
|
||||
// We are not working on this copy, get from database
|
||||
re := regexp.MustCompile("page:([^/]*)/([^/]*)/(.*)")
|
||||
m := re.FindStringSubmatch(reference)
|
||||
wref := m[1]
|
||||
pref := m[3]
|
||||
pa := GetPage(wref, pref)
|
||||
entry = entry_helper(pa.Readacl, pa.Writeacl, pa.Adminacl, reference, pa)
|
||||
}
|
||||
|
||||
if strings.Index(reference, "wiki") == 0 {
|
||||
// This must be wiki!
|
||||
re := regexp.MustCompile("wiki:(.*)")
|
||||
ref := re.FindStringSubmatch(reference)[1]
|
||||
wi := GetWiki(ref)
|
||||
entry = entry_helper(wi.Readacl, wi.Writeacl, wi.Adminacl, reference, wi)
|
||||
}
|
||||
|
||||
} else {
|
||||
// It's exactly the originating item!
|
||||
entry = entry_helper(a.Readacl, a.Writeacl, a.Adminacl, reference, a)
|
||||
}
|
||||
|
||||
revel.TRACE.Printf("BuildACLEntry() returning %+v", entry)
|
||||
|
||||
return entry
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/revel/revel"
|
||||
"github.com/mikkolehtisalo/revel/acl"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Attachment struct {
|
||||
Attachment_id string
|
||||
Wiki_id string
|
||||
// pg driver works probably better with BASE64 encoding instead of handling bytea hex string
|
||||
//Attachment []byte `json:",omitempty"`
|
||||
Attachment string
|
||||
Mime string
|
||||
Filename string
|
||||
Create_user string
|
||||
Modified time.Time
|
||||
MatchedPermissions []string
|
||||
Readacl string
|
||||
Writeacl string
|
||||
Adminacl string
|
||||
Status string
|
||||
}
|
||||
|
||||
func (a *Attachment) Validate(v *revel.Validation) {
|
||||
// Required fields
|
||||
v.Required(a.Attachment_id)
|
||||
v.Required(a.Wiki_id)
|
||||
v.Required(a.Attachment)
|
||||
v.Required(a.Filename)
|
||||
v.Required(a.Modified)
|
||||
|
||||
// Match against regexp patterns
|
||||
v.Match(a.Wiki_id, regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")).Message("Wiki_id not UUID?")
|
||||
v.Match(a.Attachment_id, regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")).Message("Wiki_id not UUID?")
|
||||
|
||||
// Validate the Attachment field
|
||||
re := regexp.MustCompile("^data:([^;]*);base64,(.*)$")
|
||||
v.Match(a.Attachment, re).Message("Invalid attachment data!")
|
||||
|
||||
// Fix the content & mime filed
|
||||
if re.MatchString(a.Attachment) {
|
||||
parts := re.FindStringSubmatch(a.Attachment)
|
||||
a.Mime = parts[1]
|
||||
a.Attachment = parts[2]
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the modified timestamp
|
||||
func (a *Attachment) Save(save_activity bool) {
|
||||
revel.TRACE.Printf("Attachment Save(): %+v", a)
|
||||
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
a.Modified = time.Now()
|
||||
|
||||
_, err := db.Exec("insert into attachments(wiki_id, attachment_id, attachment, mime, filename, create_user, modified, status) values ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
a.Wiki_id, a.Attachment_id, a.Attachment, a.Mime, a.Filename, a.Create_user, a.Modified, a.Status)
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Attachment Save(): error %+v", err)
|
||||
}
|
||||
|
||||
if save_activity {
|
||||
SaveActivity(a)
|
||||
}
|
||||
}
|
||||
|
||||
func GetAttachment(wiki string, attachment string) Attachment {
|
||||
revel.TRACE.Printf("GetAttachment() wiki:%+v, attachment:%+v", wiki, attachment)
|
||||
attachments := []Attachment{}
|
||||
att := Attachment{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&attachments, "select * from attachments a1 where a1.wiki_id=uuid_in($1) and a1.status='ACTIVE' and a1.attachment_id=uuid_in($2) and not exists (select * from attachments a2 where a1.wiki_id=a2.wiki_id and a1.attachment_id=a2.attachment_id and a2.modified>a1.modified)", wiki, attachment)
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("GetAttachment(): error %+v", err)
|
||||
}
|
||||
|
||||
if len(attachments)>0 {
|
||||
att = attachments[0]
|
||||
}
|
||||
|
||||
revel.TRACE.Printf("GetAttachment() returning %+v", att)
|
||||
return att
|
||||
}
|
||||
|
||||
func GetAttachments(wiki string) []Attachment {
|
||||
revel.TRACE.Printf("GetAttachments() wiki: %+v", wiki)
|
||||
attachments := []Attachment{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&attachments, "select * from attachments a1 where a1.wiki_id=uuid_in($1) and a1.status='ACTIVE' and not exists (select * from attachments a2 where a1.wiki_id=a2.wiki_id and a1.attachment_id=a2.attachment_id and a2.modified>a1.modified)", wiki)
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("GetAttachments(): error %+v", err)
|
||||
}
|
||||
|
||||
revel.TRACE.Printf("GetAttachments() returning: %+v", attachments)
|
||||
return attachments
|
||||
}
|
||||
|
||||
func DeleteAttachments(wiki string, user string) {
|
||||
revel.TRACE.Printf("DeleteAttachments() wiki: %+v, user: %+v", wiki, user)
|
||||
attachments := []Attachment{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&attachments, "select * from attachments a1 where a1.wiki_id=uuid_in($1) and a1.status='ACTIVE' and not exists (select * from attachments a2 where a1.wiki_id=a2.wiki_id and a1.attachment_id=a2.attachment_id and a2.modified>a1.modified)", wiki)
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("DeleteAttachments(): error %+v", err)
|
||||
}
|
||||
|
||||
for _, item := range attachments {
|
||||
item.Create_user = user
|
||||
item.Status = "DELETED"
|
||||
item.Save(false)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ACL stuff
|
||||
|
||||
// Build ACL entry for reference
|
||||
func (a Attachment) BuildACLEntry(reference string) acl.ACLEntry {
|
||||
revel.TRACE.Printf("BuildACLEntry() %+v", reference)
|
||||
var entry acl.ACLEntry
|
||||
|
||||
if reference != ("attachment:"+a.Wiki_id+"/"+a.Attachment_id) {
|
||||
if strings.Index(reference, "attachment") == 0 {
|
||||
// We are not working on this copy, get from database
|
||||
re := regexp.MustCompile("attachment:([^/]*)/(.*)")
|
||||
m := re.FindStringSubmatch(reference)
|
||||
wref := m[1]
|
||||
aref := m[2]
|
||||
aa := GetAttachment(wref, aref)
|
||||
entry = entry_helper(aa.Readacl, aa.Writeacl, aa.Adminacl, reference, aa)
|
||||
} else {
|
||||
// This must be wiki!
|
||||
re := regexp.MustCompile("wiki:(.*)")
|
||||
ref := re.FindStringSubmatch(reference)[1]
|
||||
wi := GetWiki(ref)
|
||||
entry = entry_helper(wi.Readacl, wi.Writeacl, wi.Adminacl, reference, wi)
|
||||
}
|
||||
} else {
|
||||
// It's exactly the originating item!
|
||||
entry = entry_helper(a.Readacl, a.Writeacl, a.Adminacl, reference, a)
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
// Set the matched permissions to a variable
|
||||
func (a Attachment) SetMatched(permissions []string) interface{} {
|
||||
a.MatchedPermissions = permissions
|
||||
return a
|
||||
}
|
||||
|
||||
// Building parent information
|
||||
func (a Attachment) BuildACLParent() string {
|
||||
return "wiki:"+a.Wiki_id
|
||||
}
|
||||
|
||||
// Wiki+attachment ids...
|
||||
func (a Attachment) BuildACLReference() string {
|
||||
return "attachment:"+a.Wiki_id+"/"+a.Attachment_id
|
||||
}
|
||||
|
||||
// Set on data
|
||||
func (a Attachment) BuildACLInheritation() bool {
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"fmt"
|
||||
"github.com/jmoiron/sqlx"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/mikkolehtisalo/revel/acl"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
db_user string
|
||||
db_password string
|
||||
db_name string
|
||||
)
|
||||
|
||||
// Initialize settings from app.conf
|
||||
func init() {
|
||||
revel.OnAppStart(func() {
|
||||
var ok bool
|
||||
if db_user, ok = revel.Config.String("db.user"); !ok {
|
||||
panic(fmt.Errorf("Unable to read db.user"))
|
||||
}
|
||||
if db_password, ok = revel.Config.String("db.password"); !ok {
|
||||
panic(fmt.Errorf("Unable to read db.password"))
|
||||
}
|
||||
if db_name , ok = revel.Config.String("db.name"); !ok {
|
||||
panic(fmt.Errorf("Unable to read db.name"))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func checkError(err error, s string) {
|
||||
// Syslogger
|
||||
//logger, _ := syslog.New(syslog.LOG_ERR, "SyncServer")
|
||||
//defer logger.Close()
|
||||
|
||||
if err != nil {
|
||||
//logger.Err(fmt.Sprintf("%s: %s", s, err))
|
||||
panic(fmt.Sprintf("%s: %s", s, err))
|
||||
}
|
||||
}
|
||||
|
||||
// Open new database connection
|
||||
func get_db() *sqlx.DB {
|
||||
connstring := fmt.Sprintf("user=%s password='%s' dbname=%s sslmode=disable", db_user, db_password, db_name)
|
||||
db, err := sqlx.Open("postgres", connstring)
|
||||
checkError(err, "sqlx.Open")
|
||||
return db
|
||||
}
|
||||
|
||||
// Common helper for building acl entries specific to this application
|
||||
func entry_helper(read string, write string, admin string, reference string, tgt acl.Filterable) acl.ACLEntry {
|
||||
entry := acl.ACLEntry{}
|
||||
|
||||
read_acl := acl.BuildPermissionACLs("read", strings.Split(read, ","))
|
||||
write_acl := acl.BuildPermissionACLs("write", strings.Split(write, ","))
|
||||
admin_acl := acl.BuildPermissionACLs("admin", strings.Split(admin, ","))
|
||||
acls := append(read_acl, write_acl...)
|
||||
acls = append(acls, admin_acl...)
|
||||
entry.ACLs = acls
|
||||
|
||||
entry.ObjReference = reference
|
||||
entry.Inheritation = tgt.BuildACLInheritation()
|
||||
entry.Parent = tgt.BuildACLParent()
|
||||
|
||||
return entry
|
||||
}
|
|
@ -0,0 +1,279 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/revel/revel"
|
||||
"github.com/mikkolehtisalo/revel/deXSS"
|
||||
"time"
|
||||
"regexp"
|
||||
"github.com/mikkolehtisalo/revel/acl"
|
||||
"strings"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
allowed map[string]string
|
||||
)
|
||||
|
||||
func init() {
|
||||
allowed = make(map[string]string)
|
||||
// This is actually what most basic editing functions of CKEditor require
|
||||
allowed["p"] = "class,id"
|
||||
allowed["div"] = "class,id"
|
||||
allowed["h1"] = "class,id"
|
||||
allowed["h2"] = "class,id"
|
||||
allowed["h3"] = "class,id"
|
||||
allowed["ul"] = "class,id"
|
||||
allowed["li"] = "class,id"
|
||||
allowed["a"] = "class,id,href,rel"
|
||||
allowed["img"] = "class,id,src,alt,hspace,vspace,width,height"
|
||||
allowed["span"] = "class,id,style"
|
||||
}
|
||||
|
||||
type ContentField struct {
|
||||
Contentfield_id string
|
||||
Wiki_id string
|
||||
Content string
|
||||
Contentwithmacros string
|
||||
Modified time.Time
|
||||
Status string
|
||||
Create_user string
|
||||
MatchedPermissions []string
|
||||
// Not really used, but required for handling the acls
|
||||
Readacl string
|
||||
Writeacl string
|
||||
Adminacl string
|
||||
}
|
||||
|
||||
func (c *ContentField) Validate(v *revel.Validation) {
|
||||
// Required fields
|
||||
v.Required(c.Contentfield_id)
|
||||
v.Required(c.Wiki_id)
|
||||
|
||||
// Match against regexp patterns
|
||||
v.Match(c.Contentfield_id, regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")).Message("Contentfield_id not UUID?")
|
||||
v.Match(c.Wiki_id, regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")).Message("Wiki_id not UUID?")
|
||||
|
||||
// Clean the HTML
|
||||
c.Content = deXSS.FilterHTML(c.Content, allowed, true)
|
||||
}
|
||||
|
||||
func toc_page(p Page, mode string, co map[string]interface {}) string {
|
||||
toc := ""
|
||||
|
||||
level := p.Depth + 1
|
||||
children := Get_children(p)
|
||||
filtered := acl.Filter(co, []string{"read", "write", "admin"}, children, true)
|
||||
var link string
|
||||
|
||||
for _, child := range filtered {
|
||||
x := child.(Page)
|
||||
if mode == "anchor" {
|
||||
link = "<a href='#" + x.Page_id + "' class='extpageanchor'>" + x.Title + "</a>"
|
||||
} else {
|
||||
parent := ""
|
||||
if x.Depth > 0 {
|
||||
split := strings.Split(x.Path, "/")
|
||||
parent = split[len(split)-2]
|
||||
}
|
||||
link = "<a href='#' class='extpagelink' id='" + x.Wiki_id + "/" + parent + "/" + x.Page_id + "'>" + x.Title + "</a>"
|
||||
}
|
||||
|
||||
toc = toc + "<li class='level-" + fmt.Sprintf("%v", level) + "'>" + link + "</li>"
|
||||
|
||||
if len(Get_children(x)) > 0 {
|
||||
toc = toc + toc_page(x, mode, co)
|
||||
}
|
||||
}
|
||||
|
||||
return toc
|
||||
}
|
||||
|
||||
// Mode: either "anchor" or "link"
|
||||
func (c *ContentField) build_toc(mode string, co map[string]interface {}) string {
|
||||
toc := "<div class='toc'><h1>Table of contents</h1><ul>\n"
|
||||
page := GetPage(c.Wiki_id, c.Contentfield_id)
|
||||
toc = toc + toc_page(page, mode, co)
|
||||
toc = toc + "</ul></div>"
|
||||
return toc
|
||||
}
|
||||
|
||||
func include_page(p Page, co map[string]interface {}) string {
|
||||
include := ""
|
||||
|
||||
level := p.Depth
|
||||
children := Get_children(p)
|
||||
filtered := acl.Filter(co, []string{"read", "write", "admin"}, children, true)
|
||||
|
||||
for _, child := range filtered {
|
||||
p := child.(Page)
|
||||
content := GetContent(p.Wiki_id, p.Page_id, co)
|
||||
item := "<h" + fmt.Sprintf("%v", level + 1) + " class='extpagetarget' id='" + p.Page_id + "'>" + p.Title +"</h" + fmt.Sprintf("%v", level + 1) + ">\n" + content.Content + "\n"
|
||||
include = include + item
|
||||
if len(Get_children(p)) > 0 {
|
||||
include = include + include_page(p, co)
|
||||
}
|
||||
}
|
||||
|
||||
return include
|
||||
}
|
||||
|
||||
// Sets up the recursion and fires away
|
||||
func (c *ContentField) build_include(co map[string]interface {}) string {
|
||||
include := ""
|
||||
page := GetPage(c.Wiki_id, c.Contentfield_id)
|
||||
include = include + include_page(page, co)
|
||||
return include
|
||||
}
|
||||
|
||||
func (c *ContentField) run_macros(co map[string]interface {}) {
|
||||
revel.TRACE.Printf("ContentField run_macros()")
|
||||
c.Contentwithmacros = c.Content
|
||||
|
||||
toc_anchor_regexp := regexp.MustCompile("(?m)^<p>::toc_anchor(.*)$")
|
||||
toc_link_regexp := regexp.MustCompile("(?m)^<p>::toc_link(.*)$")
|
||||
children_regexp := regexp.MustCompile("(?m)^<p>::children(.*)$")
|
||||
|
||||
if toc_anchor_regexp.MatchString(c.Content) {
|
||||
revel.TRACE.Printf("TOC anchor macro found!")
|
||||
toc := c.build_toc("anchor", co)
|
||||
c.Contentwithmacros = toc_anchor_regexp.ReplaceAllString(c.Contentwithmacros, toc)
|
||||
}
|
||||
|
||||
if toc_link_regexp.MatchString(c.Content) {
|
||||
revel.TRACE.Printf("TOC link macro found!")
|
||||
toc := c.build_toc("link", co)
|
||||
c.Contentwithmacros = toc_link_regexp.ReplaceAllString(c.Contentwithmacros, toc)
|
||||
}
|
||||
|
||||
if children_regexp.MatchString(c.Content) {
|
||||
revel.TRACE.Printf("Include children macro found!")
|
||||
include := c.build_include(co)
|
||||
c.Contentwithmacros = children_regexp.ReplaceAllString(c.Contentwithmacros, include)
|
||||
}
|
||||
}
|
||||
|
||||
// Updates modified time
|
||||
func (c *ContentField) Save(save_activity bool) {
|
||||
revel.TRACE.Printf("ContentField Save(): %+v", c)
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
c.Modified = time.Now()
|
||||
|
||||
_, err := db.Exec("insert into contentfields(contentfield_id, wiki_id, content, modified, status, create_user) values ($1, $2, $3, $4, $5, $6)",
|
||||
c.Contentfield_id, c.Wiki_id, c.Content, c.Modified, c.Status, c.Create_user )
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("ContentField Save(): failed with %+v", err)
|
||||
}
|
||||
|
||||
if save_activity {
|
||||
// Save the activity just if this was page anyways
|
||||
p := GetPageAllStatuses(c.Wiki_id, c.Contentfield_id)
|
||||
SaveActivity(&p)
|
||||
}
|
||||
}
|
||||
|
||||
func DeleteContentFields(wiki_id string, user string) {
|
||||
revel.TRACE.Printf("DeleteContentFields(): wiki: %+v, user: %+v", wiki_id, user)
|
||||
|
||||
contents := []ContentField{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&contents, "select * from contentfields c1 where wiki_id=uuid_in($1) and status='ACTIVE' and not exists (select * from contentfields c2 where c1.contentfield_id=c2.contentfield_id and c1.wiki_id=c2.wiki_id and c2.modified>c1.modified)",
|
||||
wiki_id)
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Unable to get contents %+v: %+v", wiki_id, err)
|
||||
}
|
||||
|
||||
for _, item := range contents {
|
||||
item.Status = "DELETED"
|
||||
item.Create_user = user
|
||||
item.Save(false)
|
||||
}
|
||||
}
|
||||
|
||||
func GetContent(wiki_id string, content_id string, c map[string]interface {}) ContentField {
|
||||
revel.TRACE.Printf("GetContent() wiki: %+v, content: %+v", wiki_id, content_id)
|
||||
contents := []ContentField{}
|
||||
content := ContentField{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&contents, "select * from contentfields c1 where contentfield_id=uuid_in($1) and wiki_id=uuid_in($2) and status='ACTIVE' and not exists (select * from contentfields c2 where c1.contentfield_id=c2.contentfield_id and c1.wiki_id=c2.wiki_id and c2.modified>c1.modified)",
|
||||
content_id, wiki_id)
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Unable to get content %+v/%+v: %+v", wiki_id, content_id, err)
|
||||
}
|
||||
|
||||
if len(contents)>0 {
|
||||
content = contents[0]
|
||||
}
|
||||
|
||||
content.run_macros(c)
|
||||
|
||||
revel.TRACE.Printf("GetContent() returning: %+v", content)
|
||||
return content
|
||||
}
|
||||
|
||||
|
||||
// ACL stuff
|
||||
|
||||
// Build ACL entry for reference
|
||||
func (c ContentField) BuildACLEntry(reference string) acl.ACLEntry {
|
||||
revel.TRACE.Printf("BuildACLEntry() %+v", reference)
|
||||
var entry acl.ACLEntry
|
||||
|
||||
if reference != ("cf:"+c.Wiki_id+"/"+c.Contentfield_id) {
|
||||
|
||||
if strings.Index(reference, "page") == 0 {
|
||||
// We are not working on this copy, get from database
|
||||
re := regexp.MustCompile("page:([^/]*)/(.*)")
|
||||
m := re.FindStringSubmatch(reference)
|
||||
wref := m[1]
|
||||
pref := m[2]
|
||||
pa := GetPage(wref, pref)
|
||||
entry = entry_helper(pa.Readacl, pa.Writeacl, pa.Adminacl, reference, pa)
|
||||
}
|
||||
|
||||
if strings.Index(reference, "wiki") == 0 {
|
||||
// This must be wiki!
|
||||
re := regexp.MustCompile("wiki:(.*)")
|
||||
ref := re.FindStringSubmatch(reference)[1]
|
||||
wi := GetWiki(ref)
|
||||
entry = entry_helper(wi.Readacl, wi.Writeacl, wi.Adminacl, reference, wi)
|
||||
}
|
||||
|
||||
} else {
|
||||
// It's exactly the originating item!
|
||||
entry = entry_helper(c.Readacl, c.Writeacl, c.Adminacl, reference, c)
|
||||
}
|
||||
|
||||
revel.TRACE.Printf("BuildACLEntry() returning %+v", entry)
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
// Set the matched permissions to a variable
|
||||
func (c ContentField) SetMatched(permissions []string) interface{} {
|
||||
c.MatchedPermissions = permissions
|
||||
return c
|
||||
}
|
||||
|
||||
// Building parent information
|
||||
func (c ContentField) BuildACLParent() string {
|
||||
return "page:" + c.Wiki_id + "/" + c.Contentfield_id
|
||||
}
|
||||
|
||||
// Wiki+contentfield ids...
|
||||
func (c ContentField) BuildACLReference() string {
|
||||
return "cf:"+c.Wiki_id+"/"+c.Contentfield_id
|
||||
}
|
||||
|
||||
// Always inherit the parent!
|
||||
func (c ContentField) BuildACLInheritation() bool {
|
||||
return true
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
type FavoriteWiki struct {
|
||||
Username string
|
||||
Wiki_id string
|
||||
Modified time.Time
|
||||
Status string
|
||||
}
|
||||
|
||||
// Sets modified time
|
||||
func (f FavoriteWiki) Save() {
|
||||
revel.TRACE.Printf("FavoriteWiki Save(): %+v", f)
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
_, err := db.Exec("insert into favoritewikis(username, wiki_id, modified, status) values ($1, $2, $3, $4)", f.Username, f.Wiki_id, time.Now(), f.Status)
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("FavoriteWiki Save(): error %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// List all favorites of the user
|
||||
func ListFavoriteWikis(user string) []FavoriteWiki {
|
||||
revel.TRACE.Printf("ListFavoriteWikis(): %+v", user)
|
||||
favorites := []FavoriteWiki{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&favorites, "select * from favoritewikis w1 where username=$1 and status='ACTIVE' and not exists (select * from favoritewikis w2 where w1.wiki_id=w2.wiki_id and w1.username=w2.username and w2.modified>w1.modified)", user)
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("ListFavoriteWikis(): error %+v", err)
|
||||
}
|
||||
|
||||
revel.TRACE.Printf("ListFavoriteWikis() returning: %+v", favorites)
|
||||
return favorites
|
||||
}
|
||||
|
||||
func DeleteFavorites(wiki string) {
|
||||
revel.TRACE.Printf("DeleteFavorites(): %+v", wiki)
|
||||
favorites := []FavoriteWiki{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&favorites, "select * from favoritewikis w1 where wiki_id=uuid_in($1) and status='ACTIVE' and not exists (select * from favoritewikis w2 where w1.wiki_id=w2.wiki_id and w1.username=w2.username and w2.modified>w1.modified)", wiki)
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("DeleteFavorites(): error %+v", err)
|
||||
}
|
||||
|
||||
// Delete!
|
||||
for _, item := range favorites {
|
||||
item.Status = "DELETED"
|
||||
item.Save()
|
||||
}
|
||||
}
|
||||
|
||||
// Is the wiki of user already favorited?
|
||||
// No point in optimizing probably, won't get called very often
|
||||
func IsFavoriteWiki(wiki string, user string) bool {
|
||||
fav := false
|
||||
favs := ListFavoriteWikis(user)
|
||||
for _, item := range favs {
|
||||
if item.Wiki_id==wiki {
|
||||
fav = true
|
||||
}
|
||||
}
|
||||
return fav
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/revel/revel"
|
||||
)
|
||||
|
||||
type Lock struct {
|
||||
Target_id string
|
||||
Wiki_id string
|
||||
Username string
|
||||
Realname string
|
||||
Modified time.Time
|
||||
}
|
||||
|
||||
// Sets modified time
|
||||
func (l Lock) Save() {
|
||||
revel.TRACE.Printf("Lock Save(): %+v", l)
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
_, err := db.Exec("insert into locks(wiki_id, target_id, username, realname, modified) values ($1, $2, $3, $4, $5)",
|
||||
l.Wiki_id, l.Target_id, l.Username, l.Realname, time.Now())
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Lock Save(): failed with %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Deletes by target and wiki id
|
||||
func (l Lock) Delete() {
|
||||
revel.TRACE.Printf("Lock Delete(): %+v", l)
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
_, err := db.Exec("delete from locks where wiki_id=uuid_in($1) and target_id=uuid_in($2)",
|
||||
l.Wiki_id, l.Target_id)
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Lock Delete(): failed with %+v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func GetLock(wiki string, target string) Lock {
|
||||
revel.TRACE.Printf("GetLock(): target: %+v wiki: %+v", target, wiki)
|
||||
locks := []Lock{}
|
||||
lock := Lock{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
db.Select(&locks, "select * from locks where wiki_id=uuid_in($1) and target_id=uuid_in($2)",
|
||||
wiki, target)
|
||||
if len(locks)>0 {
|
||||
lock = locks[0]
|
||||
}
|
||||
return lock
|
||||
}
|
|
@ -0,0 +1,266 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/revel/revel"
|
||||
"github.com/mikkolehtisalo/revel/acl"
|
||||
"regexp"
|
||||
"html"
|
||||
"strings"
|
||||
"fmt"
|
||||
//. "github.com/mikkolehtisalo/revel/common"
|
||||
)
|
||||
|
||||
type Page struct {
|
||||
Page_id string
|
||||
Wiki_id string
|
||||
Path string
|
||||
Title string
|
||||
Create_user string
|
||||
Readacl string
|
||||
Writeacl string
|
||||
Adminacl string
|
||||
Stopinheritation bool
|
||||
Index int
|
||||
Depth int
|
||||
Status string
|
||||
Modified time.Time
|
||||
MatchedPermissions []string
|
||||
Loaded bool `json:"loaded"`
|
||||
}
|
||||
|
||||
// Sets modified time
|
||||
// Updates depth automatically
|
||||
func (p *Page) Save(save_activity bool) {
|
||||
revel.TRACE.Printf("Page Save(): %+v", p)
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
// Update depth
|
||||
update_depth(p)
|
||||
p.Modified = time.Now()
|
||||
|
||||
_, err := db.Exec("insert into pages(page_id, wiki_id, path, title, create_user, readacl, writeacl, adminacl, stopinheritation, index, depth, modified, status) values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)",
|
||||
p.Page_id, p.Wiki_id, p.Path, p.Title, p.Create_user, p.Readacl, p.Writeacl, p.Adminacl, p.Stopinheritation, p.Index, p.Depth, p.Modified, p.Status )
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Page Save(): failed with %+v", err)
|
||||
}
|
||||
|
||||
if save_activity {
|
||||
SaveActivity(p)
|
||||
}
|
||||
}
|
||||
|
||||
// Wiki id, one page id, path
|
||||
// The pages in path must either exist in database or match given single id (in case target is not already in database)
|
||||
func valid_path(w string, me string, p string) bool {
|
||||
valid := true
|
||||
split_path := strings.Split(p, "/")
|
||||
|
||||
for _, part := range split_path {
|
||||
x := GetPage(w, part)
|
||||
if part != me && x.Page_id != part {
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
func (p *Page) Validate(v *revel.Validation) {
|
||||
// Required fields
|
||||
v.Required(p.Page_id)
|
||||
v.Required(p.Wiki_id)
|
||||
v.Required(p.Path)
|
||||
v.Required(p.Title)
|
||||
|
||||
// Match against regexp patterns
|
||||
v.Match(p.Wiki_id, regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")).Message("Wiki_id not UUID?")
|
||||
v.Match(p.Page_id, regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")).Message("Page_id not UUID?")
|
||||
|
||||
// Escape HTML from the fields that might be rendered to users
|
||||
p.Title = html.EscapeString(p.Title)
|
||||
|
||||
// Validate Path
|
||||
v.Required(valid_path(p.Wiki_id, p.Page_id, p.Path)).Message(fmt.Sprintf("Path is probably invalid: %+v", p.Path))
|
||||
}
|
||||
|
||||
func has_children(page Page) bool {
|
||||
result := false
|
||||
pages := Get_children(page)
|
||||
if len(pages) > 0 {
|
||||
result = true
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Only the next level
|
||||
func Get_children(page Page) []Page {
|
||||
revel.TRACE.Printf("Page Get_children(): %+v", page)
|
||||
pages := []Page{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
path := page.Path + "/%"
|
||||
db.Select(&pages, "select * from pages p1 where p1.status='ACTIVE' and not exists (select * from pages p2 where p1.wiki_id=p2.wiki_id and p1.page_id=p2.page_id and p2.modified>p1.modified) and p1.path like $1 and p1.depth=$2 order by index", path, page.Depth + 1)
|
||||
|
||||
return pages
|
||||
}
|
||||
|
||||
// Used only from Wiki.Delete()
|
||||
func DeletePages(wiki string, user string) {
|
||||
revel.TRACE.Printf("Page DeletePages(): wiki %+v, user %+v", wiki, user)
|
||||
pages := []Page{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
db.Select(&pages, "select * from pages p1 where p1.status='ACTIVE' and p1.wiki_id=uuid_in($1) and not exists (select * from pages p2 where p1.wiki_id=p2.wiki_id and p1.page_id=p2.page_id and p2.modified>p1.modified)",
|
||||
wiki)
|
||||
|
||||
for _, page := range pages {
|
||||
DeletePage(wiki, page.Page_id, user, false)
|
||||
}
|
||||
}
|
||||
|
||||
func ListPages(wiki_id string, node string) []Page {
|
||||
revel.TRACE.Printf("ListPages() wiki_id: %+v, node: %+v", wiki_id, node)
|
||||
pages := []Page{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
if len(node) > 0 {
|
||||
// Get the parent, and find what's under it - if anything
|
||||
p := GetPage(wiki_id, node)
|
||||
path := p.Path + "/%"
|
||||
depth := p.Depth + 1
|
||||
db.Select(&pages, "select * from pages p1 where p1.status='ACTIVE' and not exists (select * from pages p2 where p1.wiki_id=p2.wiki_id and p1.page_id=p2.page_id and p2.modified>p1.modified) and p1.path like $1 and p1.depth=$2", path, depth)
|
||||
} else {
|
||||
db.Select(&pages, "select * from pages p1 where p1.status='ACTIVE' and p1.wiki_id=uuid_in($1) and p1.depth=0 and not exists (select * from pages p2 where p1.wiki_id=p2.wiki_id and p1.page_id=p2.page_id and p2.modified>p1.modified)", wiki_id)
|
||||
}
|
||||
|
||||
// Update children status
|
||||
for x, _ := range pages {
|
||||
pages[x].Loaded = !has_children(pages[x])
|
||||
revel.TRACE.Printf("Page %+v children: %+v", pages[x], has_children(pages[x]))
|
||||
}
|
||||
|
||||
revel.TRACE.Printf("ListPages returning %+v", pages)
|
||||
return pages
|
||||
}
|
||||
|
||||
func DeletePage(wiki_id string, page_id string, user string, save_activity bool) {
|
||||
revel.TRACE.Printf("DeletePage() wiki: %+v, page: %+v, user: %+v", wiki_id, page_id, user)
|
||||
|
||||
page := GetPage(wiki_id, page_id)
|
||||
pages := Get_children(page)
|
||||
// For looping
|
||||
pages = append(pages, page)
|
||||
|
||||
for _, p := range pages {
|
||||
if p.Wiki_id != "" {
|
||||
p.Status = "DELETED"
|
||||
p.Create_user = user
|
||||
p.Save(save_activity)
|
||||
|
||||
// Delete ContentField too
|
||||
cf := GetContent(p.Wiki_id, p.Page_id, nil)
|
||||
cf.Status = "DELETED"
|
||||
cf.Create_user = user
|
||||
cf.Save(save_activity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func GetPage(wiki_id string, page_id string) Page {
|
||||
revel.TRACE.Printf("Page GetPage(): page: %+v wiki: %+v", page_id, wiki_id)
|
||||
pages := []Page{}
|
||||
page := Page{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
db.Select(&pages, "select * from pages p1 where p1.wiki_id=uuid_in($1) and p1.page_id=uuid_in($2) and p1.status='ACTIVE' and not exists (select * from pages p2 where p1.wiki_id=p2.wiki_id and p1.page_id=p2.page_id and p2.modified>p1.modified)",
|
||||
wiki_id, page_id)
|
||||
if len(pages)>0 {
|
||||
page = pages[0]
|
||||
}
|
||||
return page
|
||||
}
|
||||
|
||||
func GetPageAllStatuses(wiki_id string, page_id string) Page {
|
||||
revel.TRACE.Printf("Page GetPageAllStatuses(): page: %+v wiki: %+v", page_id, wiki_id)
|
||||
pages := []Page{}
|
||||
page := Page{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
db.Select(&pages, "select * from pages p1 where p1.wiki_id=uuid_in($1) and p1.page_id=uuid_in($2) and not exists (select * from pages p2 where p1.wiki_id=p2.wiki_id and p1.page_id=p2.page_id and p2.modified>p1.modified)",
|
||||
wiki_id, page_id)
|
||||
if len(pages)>0 {
|
||||
page = pages[0]
|
||||
}
|
||||
return page
|
||||
}
|
||||
|
||||
func update_depth(page *Page) {
|
||||
split := strings.Split(page.Path, "/")
|
||||
page.Depth = len(split) - 1
|
||||
}
|
||||
|
||||
// ACL stuff
|
||||
|
||||
// Build ACL entry for reference
|
||||
func (p Page) BuildACLEntry(reference string) acl.ACLEntry {
|
||||
revel.TRACE.Printf("BuildACLEntry() %+v", reference)
|
||||
var entry acl.ACLEntry
|
||||
|
||||
if reference != ("page:"+p.Wiki_id+"/"+p.Page_id) {
|
||||
if strings.Index(reference, "page") == 0 {
|
||||
// We are not working on this copy, get from database
|
||||
re := regexp.MustCompile("page:([^/]*)/(.*)")
|
||||
m := re.FindStringSubmatch(reference)
|
||||
wref := m[1]
|
||||
pref := m[2]
|
||||
pa := GetPage(wref, pref)
|
||||
entry = entry_helper(pa.Readacl, pa.Writeacl, pa.Adminacl, reference, pa)
|
||||
} else {
|
||||
// This must be wiki!
|
||||
re := regexp.MustCompile("wiki:(.*)")
|
||||
ref := re.FindStringSubmatch(reference)[1]
|
||||
wi := GetWiki(ref)
|
||||
entry = entry_helper(wi.Readacl, wi.Writeacl, wi.Adminacl, reference, wi)
|
||||
}
|
||||
} else {
|
||||
// It's exactly the originating item!
|
||||
entry = entry_helper(p.Readacl, p.Writeacl, p.Adminacl, reference, p)
|
||||
}
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
// Set the matched permissions to a variable
|
||||
func (p Page) SetMatched(permissions []string) interface{} {
|
||||
p.MatchedPermissions = permissions
|
||||
return p
|
||||
}
|
||||
|
||||
// Building parent information
|
||||
func (p Page) BuildACLParent() string {
|
||||
if p.Depth==0 {
|
||||
return "wiki:"+p.Wiki_id
|
||||
} else {
|
||||
pslice := strings.Split(p.Path, "/")
|
||||
return "page:"+p.Wiki_id + "/" + pslice[len(pslice)-2]
|
||||
}
|
||||
}
|
||||
|
||||
// Wiki+page ids...
|
||||
func (p Page) BuildACLReference() string {
|
||||
return "page:"+p.Wiki_id+"/"+p.Page_id
|
||||
}
|
||||
|
||||
// Set on data
|
||||
func (p Page) BuildACLInheritation() bool {
|
||||
return !p.Stopinheritation
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"github.com/mikkolehtisalo/revel/ldapuserdetails"
|
||||
"fmt"
|
||||
"strings"
|
||||
"github.com/revel/revel"
|
||||
//"time"
|
||||
// _ "github.com/lib/pq"
|
||||
//"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
var (
|
||||
ldap_user_filter string = "&(objectClass=*)"
|
||||
ldap_user_uid_attr string = "*"
|
||||
ldap_user_cn_attr string = "*"
|
||||
ldap_user_base string = "dc=*,dc=*"
|
||||
ldap_group_filter string = "&(objectClass=*)"
|
||||
ldap_group_cn_attr string = "*"
|
||||
ldap_group_dn_attr string = "*"
|
||||
ldap_group_base string = "dc=*,dc=*"
|
||||
)
|
||||
|
||||
func get_c_str(name string) string {
|
||||
if tmp, ok := revel.Config.String(name); !ok {
|
||||
panic(fmt.Errorf("%s invalid", name))
|
||||
} else {
|
||||
return tmp
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
revel.OnAppStart(func() {
|
||||
ldap_user_filter = get_c_str("ldap.user_filter")
|
||||
ldap_user_base = get_c_str("ldap.user_base")
|
||||
ldap_user_uid_attr = get_c_str("ldap.user_uid_attr")
|
||||
ldap_user_cn_attr = get_c_str("ldap.user_cn_attr")
|
||||
ldap_group_filter = get_c_str("ldap.group_filter")
|
||||
ldap_group_base = get_c_str("ldap.group_base")
|
||||
ldap_group_cn_attr = get_c_str("ldap.group_cn_attr")
|
||||
ldap_group_dn_attr = get_c_str("ldap.group_dn_attr")
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
type UserGroupSearchItem struct {
|
||||
Id string
|
||||
Name string
|
||||
Type string
|
||||
}
|
||||
|
||||
func ListUserGroupSearchItems(query string) []UserGroupSearchItem {
|
||||
items := []UserGroupSearchItem{}
|
||||
l := ldapuserdetails.Get_connection()
|
||||
defer l.Close()
|
||||
|
||||
sru := ldapuserdetails.QueryLdap(ldap_user_base, strings.Replace(ldap_user_filter, "*",
|
||||
fmt.Sprintf("*%s*",query), -1), []string{ldap_user_uid_attr, ldap_user_cn_attr})
|
||||
srg := ldapuserdetails.QueryLdap(ldap_group_base, strings.Replace(ldap_group_filter, "*",
|
||||
fmt.Sprintf("*%s*",query), -1), []string{ldap_group_cn_attr, ldap_group_dn_attr})
|
||||
|
||||
for _, user := range sru.Entries {
|
||||
item := UserGroupSearchItem {
|
||||
Id: fmt.Sprintf("u:%s",user.GetAttributeValue(ldap_user_uid_attr)),
|
||||
Name: fmt.Sprintf("%s (u:%s)", user.GetAttributeValue(ldap_user_cn_attr), user.GetAttributeValue(ldap_user_uid_attr)),
|
||||
Type: "user"}
|
||||
items = append(items, item)
|
||||
}
|
||||
for _, group := range srg.Entries {
|
||||
item := UserGroupSearchItem {
|
||||
Id: fmt.Sprintf("g:%s", group.GetAttributeValue(ldap_group_cn_attr)),
|
||||
Name: fmt.Sprintf("%s (g:%s)", group.GetAttributeValue(ldap_group_cn_attr), group.GetAttributeValue(ldap_group_cn_attr)),
|
||||
Type: "group"}
|
||||
items = append(items, item)
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
|
@ -0,0 +1,160 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"time"
|
||||
"github.com/mikkolehtisalo/revel/acl"
|
||||
"strings"
|
||||
"github.com/revel/revel"
|
||||
"regexp"
|
||||
"html"
|
||||
)
|
||||
|
||||
type Wiki struct {
|
||||
Wiki_id string
|
||||
Title string
|
||||
Description string
|
||||
Create_user string
|
||||
Readacl string
|
||||
Writeacl string
|
||||
Adminacl string
|
||||
Status string
|
||||
Modified time.Time
|
||||
MatchedPermissions []string
|
||||
Favorite bool
|
||||
}
|
||||
|
||||
// Sets the modified timestamp
|
||||
func (w *Wiki) Save(save_activity bool) {
|
||||
revel.TRACE.Printf("Wiki Save(): %+v", w)
|
||||
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
w.Modified = time.Now()
|
||||
|
||||
_, err := db.Exec("insert into wikis(wiki_id, title, description, create_user, readacl, writeacl, adminacl, status, modified) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
|
||||
w.Wiki_id, w.Title, w.Description, w.Create_user, w.Readacl, w.Writeacl, w.Adminacl, w.Status, w.Modified)
|
||||
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("Wiki Save(): error %+v", err)
|
||||
}
|
||||
|
||||
if save_activity {
|
||||
SaveActivity(w)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *Wiki) Validate(v *revel.Validation) {
|
||||
// Required fields
|
||||
v.Required(w.Wiki_id)
|
||||
v.Required(w.Title)
|
||||
|
||||
// Match against regexp patterns
|
||||
v.Match(w.Wiki_id, regexp.MustCompile("[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}")).Message("Not UUID?")
|
||||
|
||||
// Escape HTML from the fields that might be rendered to users
|
||||
w.Description = html.EscapeString(w.Description)
|
||||
w.Title = html.EscapeString(w.Title)
|
||||
w.Readacl = html.EscapeString(w.Readacl)
|
||||
w.Writeacl = html.EscapeString(w.Writeacl)
|
||||
w.Adminacl = html.EscapeString(w.Adminacl)
|
||||
}
|
||||
|
||||
//All ACTIVE Wikis
|
||||
func ListWikis() []Wiki {
|
||||
revel.TRACE.Printf("ListWikis()")
|
||||
|
||||
wikis := []Wiki{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&wikis, "select * from wikis w1 where not exists (select * from wikis w2 where w2.modified>w1.modified and w1.wiki_id=w2.wiki_id) and status='ACTIVE'")
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("ListWikis() err: %+v", err)
|
||||
}
|
||||
|
||||
revel.TRACE.Printf("ListWikis() returning %+v", wikis)
|
||||
return wikis
|
||||
}
|
||||
|
||||
//Newest ACTIVE version of wiki
|
||||
func GetWiki(id string) Wiki {
|
||||
revel.TRACE.Printf("GetWiki() %+v", id)
|
||||
wikis := []Wiki{}
|
||||
wiki := Wiki{}
|
||||
db := get_db()
|
||||
defer db.Close()
|
||||
|
||||
err := db.Select(&wikis, "select * from wikis w1 where w1.wiki_id=uuid_in($1) and w1.status='ACTIVE' and not exists (select * from wikis w2 where w1.wiki_id=w2.wiki_id and w2.modified>w1.modified)", id)
|
||||
if err != nil {
|
||||
revel.ERROR.Printf("GetWiki(): error %+v", err)
|
||||
}
|
||||
|
||||
if len(wikis)>0 {
|
||||
wiki = wikis[0]
|
||||
}
|
||||
|
||||
revel.TRACE.Printf("GetWiki() returning %+v", wiki)
|
||||
return wiki
|
||||
}
|
||||
|
||||
// Goodbye, wiki!
|
||||
func (w Wiki) Delete(user string) {
|
||||
revel.TRACE.Printf("Wiki Delete() user: %+v", user)
|
||||
w.Status = "DELETED"
|
||||
w.Create_user = user
|
||||
w.Save(true)
|
||||
DeletePages(w.Wiki_id, user)
|
||||
DeleteContentFields(w.Wiki_id, user)
|
||||
DeleteAttachments(w.Wiki_id, user)
|
||||
DeleteFavorites(w.Wiki_id)
|
||||
}
|
||||
|
||||
// ACL stuff
|
||||
// ---------
|
||||
|
||||
// Build ACL entry for reference
|
||||
func (w Wiki) BuildACLEntry(reference string) acl.ACLEntry {
|
||||
entry := acl.ACLEntry{}
|
||||
|
||||
tgt := w
|
||||
if reference != ("wiki:"+w.Wiki_id) {
|
||||
// We are not working on this copy, get from database
|
||||
re := regexp.MustCompile("wiki:(.*)")
|
||||
ref := re.FindStringSubmatch(reference)[1]
|
||||
tgt = GetWiki(ref)
|
||||
}
|
||||
|
||||
// Build the ACL from tgt
|
||||
read_acl := acl.BuildPermissionACLs("read", strings.Split(tgt.Readacl, ","))
|
||||
write_acl := acl.BuildPermissionACLs("write", strings.Split(tgt.Writeacl, ","))
|
||||
admin_acl := acl.BuildPermissionACLs("admin", strings.Split(tgt.Adminacl, ","))
|
||||
acls := append(read_acl, write_acl...)
|
||||
acls = append(acls, admin_acl...)
|
||||
entry.ObjReference = reference
|
||||
entry.ACLs = acls
|
||||
entry.Inheritation = tgt.BuildACLInheritation()
|
||||
entry.Parent = tgt.BuildACLParent()
|
||||
|
||||
return entry
|
||||
}
|
||||
|
||||
// Set the matched permissions to a variable
|
||||
func (w Wiki) SetMatched(permissions []string) interface{} {
|
||||
w.MatchedPermissions = permissions
|
||||
return w
|
||||
}
|
||||
|
||||
// Just append type to the id
|
||||
func (w Wiki) BuildACLReference() string {
|
||||
return "wiki:"+w.Wiki_id
|
||||
}
|
||||
|
||||
// No wiki inherits ACL
|
||||
func (w Wiki) BuildACLInheritation() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// No wiki has parent
|
||||
func (w Wiki) BuildACLParent() string {
|
||||
return ""
|
||||
}
|
|
@ -0,0 +1,341 @@
|
|||
// GENERATED CODE - DO NOT EDIT
|
||||
package routes
|
||||
|
||||
import "github.com/revel/revel"
|
||||
|
||||
|
||||
type tWikis struct {}
|
||||
var Wikis tWikis
|
||||
|
||||
|
||||
func (_ tWikis) Create(
|
||||
wiki string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
return revel.MainRouter.Reverse("Wikis.Create", args).Url
|
||||
}
|
||||
|
||||
func (_ tWikis) Read(
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
return revel.MainRouter.Reverse("Wikis.Read", args).Url
|
||||
}
|
||||
|
||||
func (_ tWikis) Update(
|
||||
wiki string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
return revel.MainRouter.Reverse("Wikis.Update", args).Url
|
||||
}
|
||||
|
||||
func (_ tWikis) Delete(
|
||||
wiki string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
return revel.MainRouter.Reverse("Wikis.Delete", args).Url
|
||||
}
|
||||
|
||||
|
||||
type tFavoriteWikis struct {}
|
||||
var FavoriteWikis tFavoriteWikis
|
||||
|
||||
|
||||
func (_ tFavoriteWikis) Create(
|
||||
wiki string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
return revel.MainRouter.Reverse("FavoriteWikis.Create", args).Url
|
||||
}
|
||||
|
||||
func (_ tFavoriteWikis) Read(
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
return revel.MainRouter.Reverse("FavoriteWikis.Read", args).Url
|
||||
}
|
||||
|
||||
func (_ tFavoriteWikis) Delete(
|
||||
wiki string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
return revel.MainRouter.Reverse("FavoriteWikis.Delete", args).Url
|
||||
}
|
||||
|
||||
|
||||
type tUserAvatars struct {}
|
||||
var UserAvatars tUserAvatars
|
||||
|
||||
|
||||
func (_ tUserAvatars) Read(
|
||||
user string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "user", user)
|
||||
return revel.MainRouter.Reverse("UserAvatars.Read", args).Url
|
||||
}
|
||||
|
||||
|
||||
type tUserGroupSearch struct {}
|
||||
var UserGroupSearch tUserGroupSearch
|
||||
|
||||
|
||||
func (_ tUserGroupSearch) List(
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
return revel.MainRouter.Reverse("UserGroupSearch.List", args).Url
|
||||
}
|
||||
|
||||
|
||||
type tLocks struct {}
|
||||
var Locks tLocks
|
||||
|
||||
|
||||
func (_ tLocks) Create(
|
||||
wiki string,
|
||||
target string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
revel.Unbind(args, "target", target)
|
||||
return revel.MainRouter.Reverse("Locks.Create", args).Url
|
||||
}
|
||||
|
||||
func (_ tLocks) Read(
|
||||
wiki string,
|
||||
target string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
revel.Unbind(args, "target", target)
|
||||
return revel.MainRouter.Reverse("Locks.Read", args).Url
|
||||
}
|
||||
|
||||
func (_ tLocks) Delete(
|
||||
wiki string,
|
||||
target string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
revel.Unbind(args, "target", target)
|
||||
return revel.MainRouter.Reverse("Locks.Delete", args).Url
|
||||
}
|
||||
|
||||
|
||||
type tPages struct {}
|
||||
var Pages tPages
|
||||
|
||||
|
||||
func (_ tPages) Create(
|
||||
wiki string,
|
||||
page string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
revel.Unbind(args, "page", page)
|
||||
return revel.MainRouter.Reverse("Pages.Create", args).Url
|
||||
}
|
||||
|
||||
func (_ tPages) Read(
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
return revel.MainRouter.Reverse("Pages.Read", args).Url
|
||||
}
|
||||
|
||||
func (_ tPages) Update(
|
||||
wiki string,
|
||||
page string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
revel.Unbind(args, "page", page)
|
||||
return revel.MainRouter.Reverse("Pages.Update", args).Url
|
||||
}
|
||||
|
||||
func (_ tPages) Delete(
|
||||
wiki string,
|
||||
page string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
revel.Unbind(args, "page", page)
|
||||
return revel.MainRouter.Reverse("Pages.Delete", args).Url
|
||||
}
|
||||
|
||||
|
||||
type tAttachments struct {}
|
||||
var Attachments tAttachments
|
||||
|
||||
|
||||
func (_ tAttachments) Create(
|
||||
wiki string,
|
||||
attachment string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
revel.Unbind(args, "attachment", attachment)
|
||||
return revel.MainRouter.Reverse("Attachments.Create", args).Url
|
||||
}
|
||||
|
||||
func (_ tAttachments) Read(
|
||||
wiki string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
return revel.MainRouter.Reverse("Attachments.Read", args).Url
|
||||
}
|
||||
|
||||
func (_ tAttachments) Serve(
|
||||
wiki string,
|
||||
attachment string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
revel.Unbind(args, "attachment", attachment)
|
||||
return revel.MainRouter.Reverse("Attachments.Serve", args).Url
|
||||
}
|
||||
|
||||
func (_ tAttachments) Delete(
|
||||
wiki string,
|
||||
attachment string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
revel.Unbind(args, "attachment", attachment)
|
||||
return revel.MainRouter.Reverse("Attachments.Delete", args).Url
|
||||
}
|
||||
|
||||
|
||||
type tApp struct {}
|
||||
var App tApp
|
||||
|
||||
|
||||
func (_ tApp) Index(
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
return revel.MainRouter.Reverse("App.Index", args).Url
|
||||
}
|
||||
|
||||
|
||||
type tContentFields struct {}
|
||||
var ContentFields tContentFields
|
||||
|
||||
|
||||
func (_ tContentFields) Read(
|
||||
wiki string,
|
||||
page string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
revel.Unbind(args, "page", page)
|
||||
return revel.MainRouter.Reverse("ContentFields.Read", args).Url
|
||||
}
|
||||
|
||||
func (_ tContentFields) Update(
|
||||
wiki string,
|
||||
page string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "wiki", wiki)
|
||||
revel.Unbind(args, "page", page)
|
||||
return revel.MainRouter.Reverse("ContentFields.Update", args).Url
|
||||
}
|
||||
|
||||
|
||||
type tActivities struct {}
|
||||
var Activities tActivities
|
||||
|
||||
|
||||
func (_ tActivities) Read(
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
return revel.MainRouter.Reverse("Activities.Read", args).Url
|
||||
}
|
||||
|
||||
|
||||
type tTestRunner struct {}
|
||||
var TestRunner tTestRunner
|
||||
|
||||
|
||||
func (_ tTestRunner) Index(
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
return revel.MainRouter.Reverse("TestRunner.Index", args).Url
|
||||
}
|
||||
|
||||
func (_ tTestRunner) Run(
|
||||
suite string,
|
||||
test string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "suite", suite)
|
||||
revel.Unbind(args, "test", test)
|
||||
return revel.MainRouter.Reverse("TestRunner.Run", args).Url
|
||||
}
|
||||
|
||||
func (_ tTestRunner) List(
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
return revel.MainRouter.Reverse("TestRunner.List", args).Url
|
||||
}
|
||||
|
||||
|
||||
type tStatic struct {}
|
||||
var Static tStatic
|
||||
|
||||
|
||||
func (_ tStatic) Serve(
|
||||
prefix string,
|
||||
filepath string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "prefix", prefix)
|
||||
revel.Unbind(args, "filepath", filepath)
|
||||
return revel.MainRouter.Reverse("Static.Serve", args).Url
|
||||
}
|
||||
|
||||
func (_ tStatic) ServeModule(
|
||||
moduleName string,
|
||||
prefix string,
|
||||
filepath string,
|
||||
) string {
|
||||
args := make(map[string]string)
|
||||
|
||||
revel.Unbind(args, "moduleName", moduleName)
|
||||
revel.Unbind(args, "prefix", prefix)
|
||||
revel.Unbind(args, "filepath", filepath)
|
||||
return revel.MainRouter.Reverse("Static.ServeModule", args).Url
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,375 @@
|
|||
// GENERATED CODE - DO NOT EDIT
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"reflect"
|
||||
"github.com/revel/revel"
|
||||
_ "github.com/mikkolehtisalo/iw/app"
|
||||
controllers "github.com/mikkolehtisalo/iw/app/controllers"
|
||||
_ "github.com/mikkolehtisalo/iw/app/models"
|
||||
tests "github.com/mikkolehtisalo/iw/tests"
|
||||
controllers1 "github.com/revel/revel/modules/static/app/controllers"
|
||||
_ "github.com/revel/revel/modules/testrunner/app"
|
||||
controllers0 "github.com/revel/revel/modules/testrunner/app/controllers"
|
||||
)
|
||||
|
||||
var (
|
||||
runMode *string = flag.String("runMode", "", "Run mode.")
|
||||
port *int = flag.Int("port", 0, "By default, read from app.conf")
|
||||
importPath *string = flag.String("importPath", "", "Go Import Path for the app.")
|
||||
srcPath *string = flag.String("srcPath", "", "Path to the source root.")
|
||||
|
||||
// So compiler won't complain if the generated code doesn't reference reflect package...
|
||||
_ = reflect.Invalid
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
revel.Init(*runMode, *importPath, *srcPath)
|
||||
revel.INFO.Println("Running revel server")
|
||||
|
||||
revel.RegisterController((*controllers.Wikis)(nil),
|
||||
[]*revel.MethodType{
|
||||
&revel.MethodType{
|
||||
Name: "Create",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Read",
|
||||
Args: []*revel.MethodArg{
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Update",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Delete",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
revel.RegisterController((*controllers.FavoriteWikis)(nil),
|
||||
[]*revel.MethodType{
|
||||
&revel.MethodType{
|
||||
Name: "Create",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Read",
|
||||
Args: []*revel.MethodArg{
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Delete",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
revel.RegisterController((*controllers.UserAvatars)(nil),
|
||||
[]*revel.MethodType{
|
||||
&revel.MethodType{
|
||||
Name: "Read",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "user", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
revel.RegisterController((*controllers.UserGroupSearch)(nil),
|
||||
[]*revel.MethodType{
|
||||
&revel.MethodType{
|
||||
Name: "List",
|
||||
Args: []*revel.MethodArg{
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
revel.RegisterController((*controllers.Locks)(nil),
|
||||
[]*revel.MethodType{
|
||||
&revel.MethodType{
|
||||
Name: "Create",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "target", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Read",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "target", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Delete",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "target", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
revel.RegisterController((*controllers.Pages)(nil),
|
||||
[]*revel.MethodType{
|
||||
&revel.MethodType{
|
||||
Name: "Create",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "page", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Read",
|
||||
Args: []*revel.MethodArg{
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Update",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "page", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Delete",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "page", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
revel.RegisterController((*controllers.Attachments)(nil),
|
||||
[]*revel.MethodType{
|
||||
&revel.MethodType{
|
||||
Name: "Create",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "attachment", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Read",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Serve",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "attachment", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Delete",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "attachment", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
revel.RegisterController((*controllers.App)(nil),
|
||||
[]*revel.MethodType{
|
||||
&revel.MethodType{
|
||||
Name: "Index",
|
||||
Args: []*revel.MethodArg{
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
10: []string{
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
revel.RegisterController((*controllers.ContentFields)(nil),
|
||||
[]*revel.MethodType{
|
||||
&revel.MethodType{
|
||||
Name: "Read",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "page", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Update",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "wiki", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "page", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
revel.RegisterController((*controllers.Activities)(nil),
|
||||
[]*revel.MethodType{
|
||||
&revel.MethodType{
|
||||
Name: "Read",
|
||||
Args: []*revel.MethodArg{
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
revel.RegisterController((*controllers0.TestRunner)(nil),
|
||||
[]*revel.MethodType{
|
||||
&revel.MethodType{
|
||||
Name: "Index",
|
||||
Args: []*revel.MethodArg{
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
46: []string{
|
||||
"testSuites",
|
||||
},
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "Run",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "suite", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "test", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
69: []string{
|
||||
"error",
|
||||
},
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "List",
|
||||
Args: []*revel.MethodArg{
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
revel.RegisterController((*controllers1.Static)(nil),
|
||||
[]*revel.MethodType{
|
||||
&revel.MethodType{
|
||||
Name: "Serve",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "prefix", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "filepath", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
&revel.MethodType{
|
||||
Name: "ServeModule",
|
||||
Args: []*revel.MethodArg{
|
||||
&revel.MethodArg{Name: "moduleName", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "prefix", Type: reflect.TypeOf((*string)(nil)) },
|
||||
&revel.MethodArg{Name: "filepath", Type: reflect.TypeOf((*string)(nil)) },
|
||||
},
|
||||
RenderArgNames: map[int][]string{
|
||||
},
|
||||
},
|
||||
|
||||
})
|
||||
|
||||
revel.DefaultValidationKeys = map[string]map[int]string{
|
||||
"github.com/mikkolehtisalo/iw/app/models.(*Attachment).Validate": {
|
||||
30: "a.Attachment_id",
|
||||
31: "a.Wiki_id",
|
||||
32: "a.Attachment",
|
||||
33: "a.Filename",
|
||||
34: "a.Modified",
|
||||
37: "a.Wiki_id",
|
||||
38: "a.Attachment_id",
|
||||
42: "a.Attachment",
|
||||
},
|
||||
"github.com/mikkolehtisalo/iw/app/models.(*ContentField).Validate": {
|
||||
49: "c.Contentfield_id",
|
||||
50: "c.Wiki_id",
|
||||
53: "c.Contentfield_id",
|
||||
54: "c.Wiki_id",
|
||||
},
|
||||
"github.com/mikkolehtisalo/iw/app/models.(*Page).Validate": {
|
||||
73: "p.Page_id",
|
||||
74: "p.Wiki_id",
|
||||
75: "p.Path",
|
||||
76: "p.Title",
|
||||
79: "p.Wiki_id",
|
||||
80: "p.Page_id",
|
||||
},
|
||||
"github.com/mikkolehtisalo/iw/app/models.(*Wiki).Validate": {
|
||||
48: "w.Wiki_id",
|
||||
49: "w.Title",
|
||||
52: "w.Wiki_id",
|
||||
},
|
||||
}
|
||||
revel.TestSuites = []interface{}{
|
||||
(*tests.AppTest)(nil),
|
||||
}
|
||||
|
||||
revel.Run(*port)
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>Wikis</title>
|
||||
<link rel="stylesheet" type="text/css" href="/static/extjs/resources/ext-theme-neptune/ext-theme-neptune-all.css" />
|
||||
<link rel="stylesheet" type="text/css" href="/static/css/wikis.css" />
|
||||
<script type="text/javascript">
|
||||
sessionStorage.iw_csrf_token = {{ .csrf_token }};
|
||||
</script>
|
||||
<script type="text/javascript" src="/static/extjs/ext-all.js"></script>
|
||||
<script type="text/javascript" src="/static/ckeditor/ckeditor.js"></script>
|
||||
<script type="text/javascript" src="/static/js/app.js"></script>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
|
@ -0,0 +1,64 @@
|
|||
<style type="text/css">
|
||||
#sidebar {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top:69px;
|
||||
max-width: 75%;
|
||||
z-index: 1000;
|
||||
background-color: #fee;
|
||||
border: thin solid grey;
|
||||
padding: 10px;
|
||||
}
|
||||
#toggleSidebar {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
top: 50px;
|
||||
background-color: #fee;
|
||||
}
|
||||
|
||||
</style>
|
||||
<div id="sidebar" style="display:none;">
|
||||
<h4>Available pipelines</h4>
|
||||
<dl>
|
||||
{{ range $index, $value := .}}
|
||||
<dt>{{$index}}</dt>
|
||||
<dd>{{$value}}</dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
<h4>Flash</h4>
|
||||
<dl>
|
||||
{{ range $index, $value := .flash}}
|
||||
<dt>{{$index}}</dt>
|
||||
<dd>{{$value}}</dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
|
||||
<h4>Errors</h4>
|
||||
<dl>
|
||||
{{ range $index, $value := .errors}}
|
||||
<dt>{{$index}}</dt>
|
||||
<dd>{{$value}}</dd>
|
||||
{{end}}
|
||||
</dl>
|
||||
</div>
|
||||
<a id="toggleSidebar" href="#" class="toggles"><i class="icon-chevron-left"></i></a>
|
||||
|
||||
<script>
|
||||
$sidebar = 0;
|
||||
$('#toggleSidebar').click(function() {
|
||||
if ($sidebar === 1) {
|
||||
$('#sidebar').hide();
|
||||
$('#toggleSidebar i').addClass('icon-chevron-left');
|
||||
$('#toggleSidebar i').removeClass('icon-chevron-right');
|
||||
$sidebar = 0;
|
||||
}
|
||||
else {
|
||||
$('#sidebar').show();
|
||||
$('#toggleSidebar i').addClass('icon-chevron-right');
|
||||
$('#toggleSidebar i').removeClass('icon-chevron-left');
|
||||
$sidebar = 1;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Not found</title>
|
||||
</head>
|
||||
<body>
|
||||
{{if eq .RunMode "dev"}}
|
||||
{{template "errors/404-dev.html" .}}
|
||||
{{else}}
|
||||
{{with .Error}}
|
||||
<h1>
|
||||
{{.Title}}
|
||||
</h1>
|
||||
<p>
|
||||
{{.Description}}
|
||||
</p>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,16 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Application error</title>
|
||||
</head>
|
||||
<body>
|
||||
{{if eq .RunMode "dev"}}
|
||||
{{template "errors/500-dev.html" .}}
|
||||
{{else}}
|
||||
<h1>Oops, an error occured</h1>
|
||||
<p>
|
||||
This exception has been logged.
|
||||
</p>
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,18 @@
|
|||
{{if .flash.success}}
|
||||
<div class="alert alert-success">
|
||||
{{.flash.success}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{if or .errors .flash.error}}
|
||||
<div class="alert alert-error">
|
||||
{{if .flash.error}}
|
||||
{{.flash.error}}
|
||||
{{end}}
|
||||
<ul style="margin-top:10px;">
|
||||
{{range .errors}}
|
||||
<li>{{.}}</li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
{{end}}
|
|
@ -0,0 +1,5 @@
|
|||
{{if eq .RunMode "dev"}}
|
||||
{{template "debug.html" .}}
|
||||
{{end}}
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>{{.title}}</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" type="text/css" href="/public/css/bootstrap.css">
|
||||
<link rel="shortcut icon" type="image/png" href="/public/img/favicon.png">
|
||||
<script src="/public/js/jquery-1.9.1.min.js" type="text/javascript" charset="utf-8"></script>
|
||||
{{range .moreStyles}}
|
||||
<link rel="stylesheet" type="text/css" href="/public/{{.}}">
|
||||
{{end}}
|
||||
{{range .moreScripts}}
|
||||
<script src="/public/{{.}}" type="text/javascript" charset="utf-8"></script>
|
||||
{{end}}
|
||||
</head>
|
||||
<body>
|
|
@ -0,0 +1,22 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDjzCCAnegAwIBAgIBATANBgkqhkiG9w0BAQsFADA2MRQwEgYDVQQKEwtMT0NB
|
||||
TERPTUFJTjEeMBwGA1UEAxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEzMDgx
|
||||
OTIwMzIxNloXDTMzMDgxOTIwMzIxNlowNjEUMBIGA1UEChMLTE9DQUxET01BSU4x
|
||||
HjAcBgNVBAMTFUNlcnRpZmljYXRlIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEB
|
||||
BQADggEPADCCAQoCggEBAJezjGfDZW6jjb6FNW67za4w6UO1eanl8kWGGY5WW0x+
|
||||
XCn/jZ+6qnOUsXuhtPikGjP3dUZGlo9vifJkPJ+Vz/ly2I5e2Qg8iVQMcmpJK02h
|
||||
FjfUF6lLH+rA8zbKer46Xh5HYJMX7ZaO/j6u6mK/mcUFZcwe3uSJXdM35c+UkwqI
|
||||
aw3L+UfX4fkZd4vNsLpKquetW3UjcdvpgdlN/iFZJ/S6LQaXfa8C3in3tO5qjBp9
|
||||
Zrvqx5EJNzt9YQ01JUyT1ggPDkLVC5eW4+j5hGxPAS7XBaptRZ/Jh6+wWFVJbdwU
|
||||
ZQqRiRPYjZFBIjN6VleCB/eXPyuwrDAZ0XTe61qc5acCAwEAAaOBpzCBpDAfBgNV
|
||||
HSMEGDAWgBQm0OcmrJV9Ai4zjhF7z0O5lyQiYzAPBgNVHRMBAf8EBTADAQH/MA4G
|
||||
A1UdDwEB/wQEAwIBxjAdBgNVHQ4EFgQUJtDnJqyVfQIuM44Re89DuZckImMwQQYI
|
||||
KwYBBQUHAQEENTAzMDEGCCsGAQUFBzABhiVodHRwOi8vZnJlZWlwYS5sb2NhbGRv
|
||||
bWFpbjo4MC9jYS9vY3NwMA0GCSqGSIb3DQEBCwUAA4IBAQCRQTbAOdonC4p+2b9d
|
||||
hzh2GAKBVx7w6ifFUMFNOWNZb7ocikJTTS6hSPJm87UqyKiBD8C6bQW4lCVMCD9z
|
||||
VrNVF3wHq2qWWAkfSv/UViXCI25GyWqvNDDgHLRxR5jnMJiYzZbpuSV6xjfpk9Hx
|
||||
zuuVtRxjS3e/RfbjmwRKQ3Tnq5bcXd+MhD+H7plgsoQz6DjQQMNwypurOo0o7GTO
|
||||
+N9AIasommtJ6DZkuXn2oNhIdWoMNo6ku6gXkhg86LaY4Hg/XWiUDoOG6djHtG47
|
||||
UKvsG4p0fBwlFjRBhNEF19D5eRQWKWjqBdOJm2kFRasjGLCxu6px7SCICKqdMYgp
|
||||
ChPp
|
||||
-----END CERTIFICATE-----
|
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIDlzCCAn+gAwIBAgIBDDANBgkqhkiG9w0BAQsFADA2MRQwEgYDVQQKEwtMT0NB
|
||||
TERPTUFJTjEeMBwGA1UEAxMVQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTEzMDkx
|
||||
NTE3MDc0NloXDTE1MDkxNjE3MDc0NlowMDEUMBIGA1UEChMLTE9DQUxET01BSU4x
|
||||
GDAWBgNVBAMTD2Rldi5sb2NhbGRvbWFpbjCCASIwDQYJKoZIhvcNAQEBBQADggEP
|
||||
ADCCAQoCggEBANda0OEUpTFF5O/uXixPfHxDjh4C1GCiMJuYad4R+62ukqgVMUG3
|
||||
cEfbTbFyAlqWZhnH1xaI3tbJq3jRrWpWPxtMQXr3QTuHgczIk76h14wvo4/1hqVu
|
||||
iqmMW+wiqIGJljeeCwmMSYUaItgeyTn7IrdS76N8RvdMMXproA48RN5Z8jZ4Ophf
|
||||
wJnU5XMuN80BYh0bt8gOVePIJU4OD/LejgmSR0wW3QlrtjJWStR0apz10M//+YEG
|
||||
MLa3NizIaZ6q/Wh4aFVhmU6C+Sw81E5ZXRvwlAmtwEjC+UJO8d/onjc22fwPgyJ2
|
||||
Mke0GnafqE+WJjILt07ywoJezbzxOp+0B5UCAwEAAaOBtTCBsjAfBgNVHSMEGDAW
|
||||
gBQm0OcmrJV9Ai4zjhF7z0O5lyQiYzBBBggrBgEFBQcBAQQ1MDMwMQYIKwYBBQUH
|
||||
MAGGJWh0dHA6Ly9mcmVlaXBhLmxvY2FsZG9tYWluOjgwL2NhL29jc3AwDgYDVR0P
|
||||
AQH/BAQDAgTwMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAdBgNVHQ4E
|
||||
FgQUMnIHV1I1+aO0Fr2KxMTlmULW0ZAwDQYJKoZIhvcNAQELBQADggEBAEXVkeMU
|
||||
osp7VpUnlktYpqh0Lmznh3r2Y3DgKSjQsJ6T4YhsJr1xgPAe8pqsrYY06OHho4AR
|
||||
T4tlYalYrVEFa1u/Fe3CQgDpQSWSnnCVS0i8Oyak3kJ7ZT8cQd1lmvwlgVVlHSx/
|
||||
HZByvgQPCEG2UVmIgAbp8fNj0j3w5f2bx0uWAHB0to+IOlz3mHXeB84Q3eJYg3+R
|
||||
9pzjQ/Jmg4YIMAZl4y7TiLNXv2VrnQqk83L5It8kpkRSB35bjoeZ+i4aGVZ8vI17
|
||||
vkZ2pYryQvAHRjzdWNch9RjlzSwTQTpn8CInMwkBIOw/35PiFuvGKtuWb2xII+S2
|
||||
gzp1k0d+tDAiwno=
|
||||
-----END CERTIFICATE-----
|
||||
|
Binary file not shown.
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDXWtDhFKUxReTv
|
||||
7l4sT3x8Q44eAtRgojCbmGneEfutrpKoFTFBt3BH202xcgJalmYZx9cWiN7Wyat4
|
||||
0a1qVj8bTEF690E7h4HMyJO+odeML6OP9YalboqpjFvsIqiBiZY3ngsJjEmFGiLY
|
||||
Hsk5+yK3Uu+jfEb3TDF6a6AOPETeWfI2eDqYX8CZ1OVzLjfNAWIdG7fIDlXjyCVO
|
||||
Dg/y3o4JkkdMFt0Ja7YyVkrUdGqc9dDP//mBBjC2tzYsyGmeqv1oeGhVYZlOgvks
|
||||
PNROWV0b8JQJrcBIwvlCTvHf6J43Ntn8D4MidjJHtBp2n6hPliYyC7dO8sKCXs28
|
||||
8TqftAeVAgMBAAECggEALefL8s7Fz9UIUiTLqkJfVNXJq5gK0XaBSgmZN585uPIn
|
||||
jPDHiSyjII4c56BX5Bt/NCunK8SNS9e9V4nKn4o29YixxJaELz5Zat+ursos+lMF
|
||||
WKCLzSPdJvsmdRWGOeasQLrNu4FOArNpjgwx9MwVwGJLdtv8/KYfPetHOQGb5JF/
|
||||
mHxCAeMb9b9yo+bT9Fp8PSvZrTiFddzpJyVNjKQXZ7dzowbx7J6jQXWGHybakzvv
|
||||
XeMr6VXKFdg0yvCELoJLarLyYDf21c/wpZUSISGYQgBtu4eqN5A0hOFkSIxla1YB
|
||||
s9l8TL2QVlebQxVtYe1KPXF39WDiFC/L4Rnbo1fZwQKBgQD2uqKJcN6Hga+ZPTLF
|
||||
FPzOahz68WWXEM4jVhN9lnzqNG4RR5diIEs0a9VK/XWiy1pQFMUfOczgwi1cc7lL
|
||||
TWx97MqnWHzpmo3vmWySkQiP0MoxMr71+GNesDcabOswcmuXkyw2Tu0FiZ0LFb1J
|
||||
lKp1ZiPqXEX0LGORWbww5bOKKQKBgQDfcmG49UYcA+OdJVpWZ0UbT3O+1ap9+7l8
|
||||
MfzCCyKQhYWjchIZZKCHDR01jmkSCIlQPyjvO5MdiBX8/u0yWJofzvo4/sGXz6/e
|
||||
2S/FmjXD9IiTgvEtWzbPlVHpFDNgzK8qoGQMdv9HIdoxFXItC22l+KkRGfLH3alu
|
||||
lN9o/pVXjQKBgQCvwaGZIJMUmlhZY9PvupSgAc0lIaLJqwn8+AsoUnQnbjPeEU6Q
|
||||
cwq/i/VD1yveTNfH5hPJu6Xrkcc4baNYNg4L4gs2jH1m91cWkAdcnP4pXIU76LdJ
|
||||
hqhawlJue5pKR6fHMOq0wSJs6Gu7gr/1U3bd7GW3RAX8+0XuO6sodeigiQKBgEux
|
||||
mssmQ6bP5nZgpfapKy7yPlqCVSwybwZUhLRTQ1VD9h6FUP5LKVo2phNzd8KFI8cM
|
||||
bliEjPSvGgvHbCDnBOcLnZZbLse3snNCjb1Z3SbRmsalz/+2a53sjn/vuOvsytYc
|
||||
2WejlBzLqfeAtVnvCij0UImbibQNMg1k3z3ZKEwtAoGASqrb26+DMhoyJwjZw+/f
|
||||
Lr+/J4ZgJgfSR6FJnGqf/rgt6RTrTDTHlVf5fZAvYY1jotPPwKp9KTagA2WO/mk0
|
||||
sqkKo89Svxzb9CYQw7k5S/o0sQ/WEnxIO8FdZhKON88eASFj1fGyv+be3w238pUK
|
||||
qhBrbZlaPU3xWpC97ElvoNE=
|
||||
-----END PRIVATE KEY-----
|
|
@ -0,0 +1,76 @@
|
|||
app.name=iw
|
||||
app.secret=OWd2Zg7A5wWomHFWkMNIwGNvS7qCAbGY8NKrSADg50bAaSU4hXemJTslFVV3Ah3Q
|
||||
http.addr=
|
||||
http.port=9000
|
||||
http.ssl=false
|
||||
http.sslcert=
|
||||
http.sslkey=
|
||||
cookie.httponly=false
|
||||
cookie.prefix=REVEL
|
||||
cookie.secure=false
|
||||
format.date=01/02/2006
|
||||
format.datetime=01/02/2006 15:04
|
||||
results.chunked=false
|
||||
|
||||
log.trace.prefix = "TRACE "
|
||||
log.info.prefix = "INFO "
|
||||
log.warn.prefix = "WARN "
|
||||
log.error.prefix = "ERROR "
|
||||
|
||||
# The default language of this application.
|
||||
i18n.default_language=en
|
||||
|
||||
module.static=github.com/revel/revel/modules/static
|
||||
|
||||
# cachesession
|
||||
# Allow the session be used only if the requests come from the same IP address
|
||||
session.iplock=false
|
||||
|
||||
# csrf
|
||||
csrf.ajax = true
|
||||
|
||||
# DB
|
||||
db.name=wiki
|
||||
db.user=wiki
|
||||
db.password=password
|
||||
|
||||
# ldap
|
||||
ldap.server=freeipa.localdomain
|
||||
ldap.port=389
|
||||
ldap.user_base=cn=users,cn=accounts,dc=localdomain
|
||||
ldap.user_filter=(&(uid=*)(objectClass=inetUser))
|
||||
ldap.user_uid_attr=uid
|
||||
ldap.user_cn_attr=cn
|
||||
ldap.user_photo_attr=photo;binary
|
||||
ldap.user_group_attr=memberOf
|
||||
ldap.group_filter=(&(cn=*)(objectClass=groupOfNames))
|
||||
ldap.group_cn_attr=cn
|
||||
ldap.group_dn_attr=dn
|
||||
ldap.user=uid=admin,cn=users,cn=accounts,dc=localdomain
|
||||
ldap.passwd=perkele123
|
||||
ldap.group_base=cn=groups,cn=accounts,dc=localdomain
|
||||
ldap.group_regexp=cn=([^,]+)
|
||||
|
||||
[dev]
|
||||
mode.dev=true
|
||||
results.pretty=true
|
||||
watch=true
|
||||
|
||||
module.testrunner = github.com/revel/revel/modules/testrunner
|
||||
|
||||
log.trace.output = off
|
||||
log.info.output = stderr
|
||||
log.warn.output = stderr
|
||||
log.error.output = stderr
|
||||
|
||||
[prod]
|
||||
mode.dev=false
|
||||
results.pretty=false
|
||||
watch=false
|
||||
|
||||
module.testrunner =
|
||||
|
||||
log.trace.output = off
|
||||
log.info.output = off
|
||||
log.warn.output = %(app.name)s.log
|
||||
log.error.output = %(app.name)s.log
|
|
@ -0,0 +1,64 @@
|
|||
# Routes
|
||||
# This file defines all application routes (Higher priority routes first)
|
||||
# ~~~~
|
||||
|
||||
module:testrunner
|
||||
|
||||
GET / App.Index
|
||||
|
||||
# Ignore favicon requests
|
||||
GET /favicon.ico 404
|
||||
|
||||
# Map static resources from the /app/static folder to the /static path
|
||||
GET /static/*filepath Static.Serve("static")
|
||||
|
||||
# Wikis
|
||||
POST /api/wikis/:wiki Wikis.Create
|
||||
GET /api/wikis Wikis.Read
|
||||
PUT /api/wikis/:wiki Wikis.Update
|
||||
DELETE /api/wikis/:wiki Wikis.Delete
|
||||
|
||||
# Favorite Wikis
|
||||
PUT /api/favoritewikis/:wiki FavoriteWikis.Create
|
||||
GET /api/favoritewikis FavoriteWikis.Read
|
||||
DELETE /api/favoritewikis/:wiki FavoriteWikis.Delete
|
||||
|
||||
# Pages
|
||||
POST /api/pages/:wiki/:page Pages.Create
|
||||
# ExtJS tree component passes the requested node as parameter "node"
|
||||
GET /api/pages/* Pages.Read
|
||||
PUT /api/pages/:wiki/:page Pages.Update
|
||||
DELETE /api/pages/:wiki/:page Pages.Delete
|
||||
|
||||
# Content fields
|
||||
# CREATE is never done from client side
|
||||
GET /api/contentfields/:wiki/:page ContentFields.Read
|
||||
PUT /api/contentfields/:wiki/:page ContentFields.Update
|
||||
# DELETE is never done from client side
|
||||
|
||||
# Attachments
|
||||
POST /api/attachments/:wiki/:attachment Attachments.Create
|
||||
# Serve an attachment (for direct links to files, images etc)
|
||||
GET /att/:wiki/:attachment Attachments.Serve
|
||||
GET /api/attachments/:wiki Attachments.Read
|
||||
# PUT is never done from client side
|
||||
DELETE /api/attachments/:wiki/:attachment Attachments.Delete
|
||||
|
||||
# Locks
|
||||
POST /api/locks/:wiki/:target Locks.Create
|
||||
GET /api/locks/:wiki/:target Locks.Read
|
||||
# PUT is never done
|
||||
DELETE /api/locks/:wiki/:target Locks.Delete
|
||||
|
||||
# Activities
|
||||
# Only GET available
|
||||
GET /api/activities Activities.Read
|
||||
|
||||
# User avatars
|
||||
GET /user/:user UserAvatars.Read
|
||||
|
||||
# UserGroupSearch
|
||||
GET /api/usergroupsearch UserGroupSearch.List
|
||||
|
||||
# Catch all
|
||||
* /:controller/:action :controller.:action
|
|
@ -0,0 +1,395 @@
|
|||
--
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = off;
|
||||
SET check_function_bodies = false;
|
||||
SET client_min_messages = warning;
|
||||
SET escape_string_warning = off;
|
||||
|
||||
SET search_path = public, pg_catalog;
|
||||
|
||||
SET default_tablespace = '';
|
||||
|
||||
SET default_with_oids = false;
|
||||
|
||||
--
|
||||
-- Name: activities; Type: TABLE; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE activities (
|
||||
activity_id uuid NOT NULL,
|
||||
"timestamp" timestamp with time zone,
|
||||
user_id character varying(32),
|
||||
user_name character varying(32),
|
||||
activity_type character varying(32),
|
||||
target_type character varying(16),
|
||||
target_title character varying(128),
|
||||
target_id character varying(128),
|
||||
readacl character varying(1024),
|
||||
writeacl character varying(1024),
|
||||
adminacl character varying(1024)
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.activities OWNER TO wiki;
|
||||
|
||||
--
|
||||
-- Name: attachments; Type: TABLE; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE attachments (
|
||||
attachment_id uuid NOT NULL,
|
||||
wiki_id uuid NOT NULL,
|
||||
attachment bytea,
|
||||
mime character varying,
|
||||
filename character varying,
|
||||
modified timestamp with time zone NOT NULL,
|
||||
status character varying,
|
||||
create_user character varying(32)
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.attachments OWNER TO wiki;
|
||||
|
||||
--
|
||||
-- Name: contentfields; Type: TABLE; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE contentfields (
|
||||
contentfield_id uuid NOT NULL,
|
||||
wiki_id uuid NOT NULL,
|
||||
content text,
|
||||
modified timestamp with time zone NOT NULL,
|
||||
status character varying,
|
||||
create_user character varying(32)
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.contentfields OWNER TO wiki;
|
||||
|
||||
--
|
||||
-- Name: favoritewikis; Type: TABLE; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE favoritewikis (
|
||||
username character varying NOT NULL,
|
||||
wiki_id uuid NOT NULL,
|
||||
modified timestamp with time zone NOT NULL,
|
||||
status character varying
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.favoritewikis OWNER TO wiki;
|
||||
|
||||
--
|
||||
-- Name: locks; Type: TABLE; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE locks (
|
||||
target_id uuid NOT NULL,
|
||||
wiki_id uuid NOT NULL,
|
||||
username character varying(32),
|
||||
realname character varying(32),
|
||||
modified timestamp with time zone
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.locks OWNER TO wiki;
|
||||
|
||||
--
|
||||
-- Name: pages; Type: TABLE; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE pages (
|
||||
page_id uuid NOT NULL,
|
||||
wiki_id uuid NOT NULL,
|
||||
path character varying(256),
|
||||
title character varying(256),
|
||||
create_user character varying(32),
|
||||
readacl character varying(32),
|
||||
writeacl character varying(32),
|
||||
adminacl character varying(32),
|
||||
stopinheritation boolean,
|
||||
index integer,
|
||||
depth integer,
|
||||
modified timestamp with time zone NOT NULL,
|
||||
status character varying
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.pages OWNER TO wiki;
|
||||
|
||||
--
|
||||
-- Name: wikis; Type: TABLE; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE TABLE wikis (
|
||||
wiki_id uuid NOT NULL,
|
||||
title character varying(128),
|
||||
description text,
|
||||
create_user character varying(32),
|
||||
readacl character varying(1024),
|
||||
writeacl character varying(1024),
|
||||
adminacl character varying(1024),
|
||||
modified timestamp with time zone NOT NULL,
|
||||
status character varying
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.wikis OWNER TO wiki;
|
||||
|
||||
--
|
||||
-- Name: activities_pkey; Type: CONSTRAINT; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY activities
|
||||
ADD CONSTRAINT activities_pkey PRIMARY KEY (activity_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: attachments_pkey; Type: CONSTRAINT; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY attachments
|
||||
ADD CONSTRAINT attachments_pkey PRIMARY KEY (attachment_id, wiki_id, modified);
|
||||
|
||||
|
||||
--
|
||||
-- Name: contentfields_pkey; Type: CONSTRAINT; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY contentfields
|
||||
ADD CONSTRAINT contentfields_pkey PRIMARY KEY (contentfield_id, wiki_id, modified);
|
||||
|
||||
|
||||
--
|
||||
-- Name: favoritewikis_pkey; Type: CONSTRAINT; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY favoritewikis
|
||||
ADD CONSTRAINT favoritewikis_pkey PRIMARY KEY (username, wiki_id, modified);
|
||||
|
||||
|
||||
--
|
||||
-- Name: locks_pkey; Type: CONSTRAINT; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY locks
|
||||
ADD CONSTRAINT locks_pkey PRIMARY KEY (target_id, wiki_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: pages_pkey; Type: CONSTRAINT; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY pages
|
||||
ADD CONSTRAINT pages_pkey PRIMARY KEY (page_id, wiki_id, modified);
|
||||
|
||||
|
||||
--
|
||||
-- Name: wikis_pkey; Type: CONSTRAINT; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY wikis
|
||||
ADD CONSTRAINT wikis_pkey PRIMARY KEY (wiki_id, modified);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_activities_activity_id; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_activities_activity_id ON activities USING btree (activity_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_activities_timestamp; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_activities_timestamp ON activities USING btree ("timestamp");
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_activities_user_id; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_activities_user_id ON activities USING btree (user_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_activities_user_name; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_activities_user_name ON activities USING btree (user_name);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_attachments_attachment_id; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_attachments_attachment_id ON attachments USING btree (attachment_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_attachments_modified; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_attachments_modified ON attachments USING btree (modified);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_attachments_status; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_attachments_status ON attachments USING btree (status);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_attachments_wiki_id; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_attachments_wiki_id ON attachments USING btree (wiki_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_contentfields_contentfield_id; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_contentfields_contentfield_id ON contentfields USING btree (contentfield_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_contentfields_modified; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_contentfields_modified ON contentfields USING btree (modified);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_contentfields_status; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_contentfields_status ON contentfields USING btree (status);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_contentfields_wiki_id; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_contentfields_wiki_id ON contentfields USING btree (wiki_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_favoritewikis_modified; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_favoritewikis_modified ON favoritewikis USING btree (modified);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_favoritewikis_status; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_favoritewikis_status ON favoritewikis USING btree (status);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_favoritewikis_username; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_favoritewikis_username ON favoritewikis USING btree (username);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_favoritewikis_wiki_id; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_favoritewikis_wiki_id ON favoritewikis USING btree (wiki_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_locks_page_id; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_locks_page_id ON locks USING btree (target_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_locks_wiki_id; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_locks_wiki_id ON locks USING btree (wiki_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_pages_modified; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_pages_modified ON pages USING btree (modified);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_pages_page_id; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_pages_page_id ON pages USING btree (page_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_pages_path; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_pages_path ON pages USING btree (path);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_pages_status; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_pages_status ON pages USING btree (status);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_pages_wiki_id; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_pages_wiki_id ON pages USING btree (wiki_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_wikis_modified; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_wikis_modified ON wikis USING btree (modified);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_wikis_status; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_wikis_status ON wikis USING btree (status);
|
||||
|
||||
|
||||
--
|
||||
-- Name: ix_wikis_wiki_id; Type: INDEX; Schema: public; Owner: wiki; Tablespace:
|
||||
--
|
||||
|
||||
CREATE INDEX ix_wikis_wiki_id ON wikis USING btree (wiki_id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: public; Type: ACL; Schema: -; Owner: postgres
|
||||
--
|
||||
|
||||
REVOKE ALL ON SCHEMA public FROM PUBLIC;
|
||||
REVOKE ALL ON SCHEMA public FROM postgres;
|
||||
GRANT ALL ON SCHEMA public TO postgres;
|
||||
GRANT ALL ON SCHEMA public TO PUBLIC;
|
||||
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 108 KiB |
|
@ -0,0 +1,7 @@
|
|||
# Sample messages file for the English language (en)
|
||||
# Message file extensions should be ISO 639-1 codes (http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
|
||||
# Sections within each message file can optionally override the defaults using ISO 3166-1 alpha-2 codes (http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2)
|
||||
# See also:
|
||||
# - http://www.rfc-editor.org/rfc/bcp/bcp47.txt
|
||||
# - http://www.w3.org/International/questions/qa-accept-lang-locales
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"CAKeyFile": "/root/go/src/iw/certificates/ca.crt",
|
||||
"MyCertificateFile": "/root/go/src/iw/certificates/my.crt",
|
||||
"MyKeyFile": "/root/go/src/iw/certificates/my.pem",
|
||||
"ValidPeers": ["dev.localdomain"],
|
||||
"ServiceAddress": "dev.localdomain:8000",
|
||||
"DBUser": "wiki",
|
||||
"DBPassword": "password",
|
||||
"DBName": "wiki2",
|
||||
"Interval": 5
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"CAKeyFile": "/root/go/src/iw/certificates/ca.crt",
|
||||
"MyCertificateFile": "/root/go/src/iw/certificates/my.crt",
|
||||
"MyKeyFile": "/root/go/src/iw/certificates/my.pem",
|
||||
"ValidPeers": ["dev.localdomain"],
|
||||
"ServiceAddress": "0.0.0.0:8000",
|
||||
"DBUser": "wiki",
|
||||
"DBPassword": "password",
|
||||
"DBName": "wiki"
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,319 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log/syslog"
|
||||
"time"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"fmt"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"iw/replication/src/common"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func checkError(err error, s string) {
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("%s: %s", s, err))
|
||||
}
|
||||
}
|
||||
|
||||
func countRows (rows *sqlx.Rows) int {
|
||||
var count = 0
|
||||
for rows.Next() {
|
||||
count ++
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
func getDB(settings common.ServiceConfiguration) *sqlx.DB {
|
||||
// Open database connection
|
||||
connstring := fmt.Sprintf("user=%s password='%s' dbname=%s sslmode=disable", settings.DBUser, settings.DBPassword, settings.DBName)
|
||||
db, err := sqlx.Open("postgres", connstring)
|
||||
checkError(err, "sql.Open")
|
||||
return db
|
||||
}
|
||||
|
||||
func itemExists (item interface{}, settings common.ServiceConfiguration) bool {
|
||||
db := getDB(settings)
|
||||
defer db.Close()
|
||||
|
||||
// Query for data
|
||||
var rows *sqlx.Rows
|
||||
// Error
|
||||
var err error
|
||||
|
||||
// Prepare for reflect
|
||||
val := reflect.ValueOf(item)
|
||||
|
||||
// Query the correct item type
|
||||
switch item.(type) {
|
||||
case common.Wiki:
|
||||
modified := val.FieldByName("Modified").Interface()
|
||||
rows, err = db.Queryx("select wiki_id, modified from wikis "+
|
||||
"where wiki_id=uuid_in($1) and modified=$2", val.FieldByName("Wiki_id").String(), modified)
|
||||
case common.Page:
|
||||
modified := val.FieldByName("Modified").Interface()
|
||||
rows, err = db.Queryx("select page_id, wiki_id, modified from pages "+
|
||||
"where page_id=uuid_in($1) and wiki_id=uuid_in($2) and modified=$3", val.FieldByName("Page_id").String(), val.FieldByName("Wiki_id").String(), modified)
|
||||
case common.ContentField:
|
||||
modified := val.FieldByName("Modified").Interface()
|
||||
rows, err = db.Queryx("select contentfield_id, wiki_id, modified from contentfields "+
|
||||
"where contentfield_id=uuid_in($1) and wiki_id=uuid_in($2) and modified=$3", val.FieldByName("Contentfield_id").String(), val.FieldByName("Wiki_id").String(), modified)
|
||||
case common.Attachment:
|
||||
modified := val.FieldByName("Modified").Interface()
|
||||
rows, err = db.Queryx("select attachment_id, wiki_id, modified from attachments "+
|
||||
"where attachment_id=uuid_in($1) and wiki_id=uuid_in($2) and modified=$3", val.FieldByName("Attachment_id").String(), val.FieldByName("Wiki_id").String(), modified)
|
||||
case common.FavoriteWiki:
|
||||
modified := val.FieldByName("Modified").Interface()
|
||||
rows, err = db.Queryx("select username, wiki_id, modified from favoritewikis "+
|
||||
"where username=$1 and wiki_id=uuid_in($2) and modified=$3", val.FieldByName("Username").String(), val.FieldByName("Wiki_id").String(), modified)
|
||||
case common.Activity:
|
||||
rows, err = db.Queryx("select activity_id from activities "+
|
||||
"where activity_id=uuid_in($1)", val.FieldByName("Activity_id").String())
|
||||
case common.Lock:
|
||||
rows, err = db.Queryx("select target_id, wiki_id from locks "+
|
||||
"where target_id=uuid_in($1) and wiki_id=uuid_in($2)", val.FieldByName("Target_id").String(), val.FieldByName("Wiki_id").String())
|
||||
}
|
||||
|
||||
checkError(err, "Queryx")
|
||||
|
||||
count := countRows(rows)
|
||||
if count == 1 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func saveItem(item interface{}, settings common.ServiceConfiguration) {
|
||||
db := getDB(settings)
|
||||
defer db.Close()
|
||||
var err error
|
||||
|
||||
switch item.(type) {
|
||||
case common.Wiki:
|
||||
_, err = db.NamedExec("INSERT INTO wikis (wiki_id, title, description, create_user, readacl, writeacl, adminacl, status, modified) VALUES "+
|
||||
"(:wiki_id, :title, :description, :create_user, :readacl, :writeacl, :adminacl, :status, :modified)", item)
|
||||
case common.Page:
|
||||
_, err = db.NamedExec("insert into pages (page_id, wiki_id, path, title, create_user, readacl, writeacl, adminacl, stopinheritation, index, depth, status, modified) values "+
|
||||
"(:page_id, :wiki_id, :path, :title, :create_user, :readacl, :writeacl, :adminacl, :stopinheritation, :index, :depth, :status, :modified)", item)
|
||||
case common.ContentField:
|
||||
_, err = db.NamedExec("insert into contentfields (contentfield_id, wiki_id, content, modified, status, create_user) values "+
|
||||
"(:contentfield_id, :wiki_id, :content, :modified, :status, :create_user)", item)
|
||||
case common.Attachment:
|
||||
_, err = db.NamedExec("insert into attachments (attachment_id, wiki_id, attachment, mime, filename, modified, status, create_user) values "+
|
||||
"(:attachment_id, :wiki_id, decode(:attachment, 'base64'), :mime, :filename, :modified, :status, :create_user)", item)
|
||||
case common.FavoriteWiki:
|
||||
_, err = db.NamedExec("insert into favoritewikis (username, wiki_id, modified, status) values "+
|
||||
"(:username, :wiki_id, :modified, :status)", item)
|
||||
case common.Activity:
|
||||
_, err = db.NamedExec("insert into activities (activity_id, timestamp, user_id, user_name, activity_type, target_type, target_title, target_id, readacl, writeacl, adminacl) values "+
|
||||
"(:activity_id, :timestamp, :user_id, :user_name, :activity_type, :target_type, :target_title, :target_id, :readacl, :writeacl, :adminacl)", item)
|
||||
case common.Lock:
|
||||
_, err = db.NamedExec("insert into locks (target_id, wiki_id, username, realname, modified values "+
|
||||
"(:target_id, :wiki_id, :username, :realname, :modified", item)
|
||||
}
|
||||
checkError(err, "execInsert")
|
||||
}
|
||||
|
||||
func mapToStruct(input map[string] interface{}, it interface{}) interface{} {
|
||||
itType := reflect.ValueOf(it).Type()
|
||||
v := reflect.New(itType).Elem()
|
||||
|
||||
for key, value := range input {
|
||||
field := v.FieldByName(key)
|
||||
if !field.IsValid() {
|
||||
// or handle as error if you don't expect unknown values
|
||||
continue
|
||||
}
|
||||
if !field.CanSet() {
|
||||
// or return an error on private fields
|
||||
continue
|
||||
}
|
||||
|
||||
switch key {
|
||||
case "Depth", "Index":
|
||||
fl := reflect.ValueOf(value).Float()
|
||||
fi := int(fl)
|
||||
field.Set(reflect.ValueOf(fi))
|
||||
case "Modified", "Timestamp":
|
||||
s := reflect.ValueOf(value).String()
|
||||
modified, terr := time.Parse(time.RFC3339Nano, s)
|
||||
checkError(terr, "Unable to parse time")
|
||||
field.Set(reflect.ValueOf(modified))
|
||||
default:
|
||||
field.Set(reflect.ValueOf(value))
|
||||
}
|
||||
|
||||
}
|
||||
return v.Interface()
|
||||
}
|
||||
|
||||
func syncItems(tr *http.Transport, settings common.ServiceConfiguration, it interface{}, queryListUrl string, queryUrl string) error {
|
||||
// Syslogger
|
||||
logger, _ := syslog.New(syslog.LOG_ERR, "SyncClient")
|
||||
defer logger.Close()
|
||||
|
||||
// Get list of items
|
||||
client := &http.Client{Transport: tr}
|
||||
result, err := client.Get("https://" + settings.ServiceAddress + queryListUrl)
|
||||
checkError(err, "syncItems Get")
|
||||
defer result.Body.Close()
|
||||
|
||||
// Read the response body
|
||||
body, err := ioutil.ReadAll(result.Body)
|
||||
checkError(err, "syncItems ReadAll")
|
||||
|
||||
// Marshal the response JSON to map
|
||||
var tmp []map[string] interface{}
|
||||
err = json.Unmarshal(body, &tmp)
|
||||
checkError(err, "syncItems Unmarshal")
|
||||
|
||||
// Parse each item from map
|
||||
for _, item := range tmp {
|
||||
// Convert to struct
|
||||
target := mapToStruct(item, it)
|
||||
val := reflect.ValueOf(target)
|
||||
// If item doesn't exist already, sync full item
|
||||
if !itemExists(target, settings) {
|
||||
logger.Info(fmt.Sprintf("Item %+v : %+v did not exist, syncing...", reflect.ValueOf(target).Type(), target))
|
||||
|
||||
var resp *http.Response
|
||||
// Get the whole item
|
||||
switch target.(type) {
|
||||
case common.Wiki:
|
||||
iface := val.FieldByName("Modified").Interface()
|
||||
modified := iface.(time.Time)
|
||||
formated := modified.Format(time.RFC3339Nano)
|
||||
resp, err = client.Get("https://" + settings.ServiceAddress + queryUrl + val.FieldByName("Wiki_id").String() + "/" + formated)
|
||||
case common.Page:
|
||||
iface := val.FieldByName("Modified").Interface()
|
||||
modified := iface.(time.Time)
|
||||
formated := modified.Format(time.RFC3339Nano)
|
||||
resp, err = client.Get("https://" + settings.ServiceAddress + queryUrl + val.FieldByName("Page_id").String() + "/" + val.FieldByName("Wiki_id").String() + "/" + formated)
|
||||
case common.ContentField:
|
||||
iface := val.FieldByName("Modified").Interface()
|
||||
modified := iface.(time.Time)
|
||||
formated := modified.Format(time.RFC3339Nano)
|
||||
resp, err = client.Get("https://" + settings.ServiceAddress + queryUrl + val.FieldByName("Contentfield_id").String() + "/" + val.FieldByName("Wiki_id").String() + "/" + formated)
|
||||
case common.Attachment:
|
||||
iface := val.FieldByName("Modified").Interface()
|
||||
modified := iface.(time.Time)
|
||||
formated := modified.Format(time.RFC3339Nano)
|
||||
resp, err = client.Get("https://" + settings.ServiceAddress + queryUrl + val.FieldByName("Attachment_id").String() + "/" + val.FieldByName("Wiki_id").String() + "/" + formated)
|
||||
case common.FavoriteWiki:
|
||||
iface := val.FieldByName("Modified").Interface()
|
||||
modified := iface.(time.Time)
|
||||
formated := modified.Format(time.RFC3339Nano)
|
||||
resp, err = client.Get("https://" + settings.ServiceAddress + queryUrl + val.FieldByName("Username").String() + "/" + val.FieldByName("Wiki_id").String() + "/" + formated)
|
||||
case common.Activity:
|
||||
resp, err = client.Get("https://" + settings.ServiceAddress + queryUrl + val.FieldByName("Activity_id").String())
|
||||
case common.Lock:
|
||||
resp, err = client.Get("https://" + settings.ServiceAddress + queryUrl + val.FieldByName("Wiki_id").String() + "/" + val.FieldByName("Target_id").String())
|
||||
}
|
||||
|
||||
checkError(err, "syncItems GetFull")
|
||||
defer resp.Body.Close()
|
||||
// Read the body of whole item
|
||||
fullBody, err := ioutil.ReadAll(resp.Body)
|
||||
checkError(err, "syncWikis ReadFullBody")
|
||||
|
||||
// Marshal to map and convert to item
|
||||
var temp2 map[string] interface{}
|
||||
err = json.Unmarshal(fullBody, &temp2)
|
||||
checkError(err, "fullItem Unmarshal")
|
||||
fullItem := mapToStruct(temp2, it)
|
||||
|
||||
logger.Debug(fmt.Sprintf("Saving item %+v", target))
|
||||
// Save to DB
|
||||
saveItem(fullItem, settings)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func syncAll(tr *http.Transport, settings common.ServiceConfiguration) {
|
||||
|
||||
logger, _ := syslog.New(syslog.LOG_ERR, "SyncClient")
|
||||
defer logger.Close()
|
||||
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
logger, _ := syslog.New(syslog.LOG_ERR, "SyncClient")
|
||||
defer logger.Close()
|
||||
switch x := e.(type) {
|
||||
case error:
|
||||
err := x
|
||||
logger.Err(fmt.Sprintf("Error: %s\n", err))
|
||||
default:
|
||||
err := fmt.Errorf("%v", x)
|
||||
logger.Err(fmt.Sprintf("Error: %s\n", err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
logger.Info("Syncing wikis...")
|
||||
syncItems(tr, settings, common.Wiki{}, "/repl/wikis", "/repl/wiki/")
|
||||
logger.Info("Syncing pages...")
|
||||
syncItems(tr, settings, common.Page{}, "/repl/pages", "/repl/page/")
|
||||
logger.Info("Syncing contentfields...")
|
||||
syncItems(tr, settings, common.ContentField{}, "/repl/contentfields", "/repl/contentfield/")
|
||||
logger.Info("Syncing attachments...")
|
||||
syncItems(tr, settings, common.Attachment{}, "/repl/attachments", "/repl/attachment/")
|
||||
logger.Info("Syncing activities...")
|
||||
syncItems(tr, settings, common.Activity{}, "/repl/activities", "/repl/activity/")
|
||||
logger.Info("Syncing favoritewikis...")
|
||||
syncItems(tr, settings, common.FavoriteWiki{}, "/repl/favoritewikis", "/repl/favoritewiki/")
|
||||
logger.Info("Syncing page locks...")
|
||||
syncItems(tr, settings, common.Lock{}, "/repl/locks", "/repl/lock/")
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Load the settings
|
||||
var settings common.ServiceConfiguration
|
||||
settings.ConfigFrom("client.json")
|
||||
|
||||
// Load my SSL key and certificate
|
||||
cert, err := tls.LoadX509KeyPair(settings.MyCertificateFile, settings.MyKeyFile)
|
||||
checkError(err, "LoadX509KeyPair")
|
||||
|
||||
// Load the CA certificate for server certificate validation
|
||||
capool := x509.NewCertPool()
|
||||
cacert, err := ioutil.ReadFile(settings.CAKeyFile)
|
||||
checkError(err, "loadCACert")
|
||||
capool.AppendCertsFromPEM(cacert)
|
||||
|
||||
// Prepare config and transport
|
||||
config := tls.Config{Certificates: []tls.Certificate{cert}, RootCAs: capool}
|
||||
tr := &http.Transport{
|
||||
TLSClientConfig: &config,
|
||||
}
|
||||
|
||||
// Prepare timer
|
||||
ticker := time.NewTicker(time.Duration(settings.Interval) * time.Second)
|
||||
quit := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case <- ticker.C:
|
||||
// Attempt to synchronize all...
|
||||
syncAll (tr, settings)
|
||||
|
||||
case <- quit:
|
||||
ticker.Stop()
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
|
||||
select {}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"CAKeyFile": "/root/go/src/iw/certificates/ca.crt",
|
||||
"MyCertificateFile": "/root/go/src/iw/certificates/my.crt",
|
||||
"MyKeyFile": "/root/go/src/iw/certificates/my.pem",
|
||||
"ValidPeers": ["dev.localdomain"],
|
||||
"ServiceAddress": "dev.localdomain:8000",
|
||||
"DBUser": "wiki",
|
||||
"DBPassword": "password",
|
||||
"DBName": "wiki2",
|
||||
"Interval": 5
|
||||
}
|
||||
|
Binary file not shown.
|
@ -0,0 +1,150 @@
|
|||
package common
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Checks whether string can be found from slice
|
||||
func stringInSlice(a string, list []string) bool {
|
||||
for _, b := range list {
|
||||
if b == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Defines the configuration file format
|
||||
type ServiceConfiguration struct {
|
||||
CAKeyFile string
|
||||
MyCertificateFile string
|
||||
MyKeyFile string
|
||||
ValidPeers []string
|
||||
ServiceAddress string
|
||||
DBUser string
|
||||
DBPassword string
|
||||
DBName string
|
||||
Interval int
|
||||
}
|
||||
|
||||
// Used to load the configuration from file
|
||||
func (l *ServiceConfiguration) ConfigFrom(path string) (error) {
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Fatalf("ReadFile: %s", err)
|
||||
return err
|
||||
}
|
||||
err = json.Unmarshal(b, &l)
|
||||
if err != nil {
|
||||
log.Fatalf("Bad json: %s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Peer CN check
|
||||
func CheckPeer(r *http.Request, settings ServiceConfiguration) error {
|
||||
var valid bool = false
|
||||
|
||||
for _,chain := range r.TLS.VerifiedChains {
|
||||
for _,certificate := range chain {
|
||||
cn := certificate.Subject.CommonName
|
||||
if stringInSlice(cn, settings.ValidPeers) {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !valid {
|
||||
return errors.New("No valid certificate received from peer!")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Types for the replication
|
||||
|
||||
type Attachment struct {
|
||||
Attachment_id string
|
||||
Wiki_id string
|
||||
// pg driver works probably better with BASE64 encoding instead of handling bytea hex string
|
||||
//Attachment []byte `json:",omitempty"`
|
||||
Attachment string `json:",omitempty"`
|
||||
Mime string `json:",omitempty"`
|
||||
Filename string `json:",omitempty"`
|
||||
Create_user string `json:",omitempty"`
|
||||
Modified time.Time
|
||||
Status string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type FavoriteWiki struct {
|
||||
Username string
|
||||
Wiki_id string
|
||||
Modified time.Time
|
||||
Status string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type ContentField struct {
|
||||
Contentfield_id string
|
||||
Wiki_id string
|
||||
Content string `json:",omitempty"`
|
||||
Contentwithmacros string `json:",omitempty"`
|
||||
Modified time.Time
|
||||
Status string `json:",omitempty"`
|
||||
Create_user string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type Wiki struct {
|
||||
Wiki_id string
|
||||
Title string `json:",omitempty"`
|
||||
Description string `json:",omitempty"`
|
||||
Create_user string `json:",omitempty"`
|
||||
Readacl string `json:",omitempty"`
|
||||
Writeacl string `json:",omitempty"`
|
||||
Adminacl string `json:",omitempty"`
|
||||
Status string `json:",omitempty"`
|
||||
Modified time.Time
|
||||
}
|
||||
|
||||
type Page struct {
|
||||
Page_id string
|
||||
Wiki_id string
|
||||
Path string `json:",omitempty"`
|
||||
Title string `json:",omitempty"`
|
||||
Create_user string `json:",omitempty"`
|
||||
Readacl string `json:",omitempty"`
|
||||
Writeacl string `json:",omitempty"`
|
||||
Adminacl string `json:",omitempty"`
|
||||
Stopinheritation bool `json:",omitempty"`
|
||||
Index int `json:",omitempty"`
|
||||
Depth int `json:",omitempty"`
|
||||
Status string `json:",omitempty"`
|
||||
Modified time.Time
|
||||
}
|
||||
|
||||
type Activity struct {
|
||||
Activity_id string
|
||||
Timestamp time.Time `json:",omitempty"`
|
||||
User_id string `json:",omitempty"`
|
||||
User_name string `json:",omitempty"`
|
||||
Activity_type string `json:",omitempty"`
|
||||
Target_type string `json:",omitempty"`
|
||||
Target_title string `json:",omitempty"`
|
||||
Target_id string `json:",omitempty"`
|
||||
Readacl string `json:",omitempty"`
|
||||
Writeacl string `json:",omitempty"`
|
||||
Adminacl string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type Lock struct {
|
||||
Target_id string
|
||||
Wiki_id string
|
||||
Username string `json:",omitempty"`
|
||||
Realname string `json:",omitempty"`
|
||||
Modified time.Time `json:",omitempty"`
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Subproject commit c6e1c3e4d5c1b1f6a849bb44f1f6c51ee17f8a2e
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 0cbf7d2d198fb08409ead5bde569c8b652bdfb87
|
Binary file not shown.
|
@ -0,0 +1,234 @@
|
|||
package main
|
||||
import (
|
||||
"fmt"
|
||||
"crypto/tls"
|
||||
"crypto/rand"
|
||||
"crypto/x509"
|
||||
"time"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net"
|
||||
"encoding/json"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"regexp"
|
||||
"reflect"
|
||||
"iw/replication/src/common"
|
||||
"log/syslog"
|
||||
)
|
||||
|
||||
func checkError(err error, s string) {
|
||||
// Syslogger
|
||||
logger, _ := syslog.New(syslog.LOG_ERR, "SyncServer")
|
||||
defer logger.Close()
|
||||
|
||||
if err != nil {
|
||||
logger.Err(fmt.Sprintf("%s: %s", s, err))
|
||||
panic(fmt.Sprintf("%s: %s", s, err))
|
||||
}
|
||||
}
|
||||
|
||||
func myHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "ALIVE")
|
||||
}
|
||||
|
||||
func buildListJSON(rows *sqlx.Rows, tgt interface{}) ([]byte, error) {
|
||||
tgtType := reflect.ValueOf(tgt).Type()
|
||||
targets := reflect.MakeSlice(reflect.SliceOf(tgtType), 0, 10)
|
||||
for rows.Next() {
|
||||
target := reflect.New(tgtType).Interface()
|
||||
err := rows.StructScan(target)
|
||||
checkError(err, "rows.Scan")
|
||||
targets = reflect.Append(targets, reflect.ValueOf(target).Elem())
|
||||
}
|
||||
checkError(rows.Err(), "rows.Err")
|
||||
jsontext, err := json.Marshal(targets.Interface())
|
||||
return jsontext, err
|
||||
}
|
||||
|
||||
func buildJSON(rows *sqlx.Rows, tgt interface{}) ([]byte, error) {
|
||||
tgtType := reflect.ValueOf(tgt).Type()
|
||||
target := reflect.New(tgtType).Interface()
|
||||
for rows.Next() {
|
||||
err := rows.StructScan(target)
|
||||
checkError(err, "rows.Scan")
|
||||
}
|
||||
checkError(rows.Err(), "rows.Err")
|
||||
jsontext, err := json.Marshal(target)
|
||||
return jsontext, err
|
||||
}
|
||||
|
||||
func uniHandler(w http.ResponseWriter, r *http.Request, t interface{}, l bool) {
|
||||
// Handle panics - for logging mostly
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
logger, _ := syslog.New(syslog.LOG_ERR, "SyncServer")
|
||||
defer logger.Close()
|
||||
switch x := e.(type) {
|
||||
case error:
|
||||
err := x
|
||||
logger.Err(fmt.Sprintf("Error: %s\n", err))
|
||||
default:
|
||||
err := fmt.Errorf("%v", x)
|
||||
logger.Err(fmt.Sprintf("Error: %s\n", err))
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// Load settings
|
||||
var settings common.ServiceConfiguration
|
||||
settings.ConfigFrom("server.json")
|
||||
|
||||
// Check the peer
|
||||
err := common.CheckPeer(r, settings)
|
||||
checkError(err, "checkPeer")
|
||||
|
||||
// Open database connection
|
||||
connstring := fmt.Sprintf("user=%s password='%s' dbname=%s sslmode=disable", settings.DBUser, settings.DBPassword, settings.DBName)
|
||||
db, err := sqlx.Open("postgres", connstring)
|
||||
checkError(err, "sql.Open")
|
||||
defer db.Close()
|
||||
|
||||
// Query for data
|
||||
var rows *sqlx.Rows
|
||||
|
||||
if l {
|
||||
// Lists
|
||||
switch t.(type) {
|
||||
case common.Wiki:
|
||||
rows, err = db.Queryx("select wiki_id, modified from wikis")
|
||||
case common.Page:
|
||||
rows, err = db.Queryx("select page_id, wiki_id, modified from pages")
|
||||
case common.ContentField:
|
||||
rows, err = db.Queryx("select contentfield_id, wiki_id, modified from contentfields")
|
||||
case common.FavoriteWiki:
|
||||
rows, err = db.Queryx("select username, wiki_id, modified from favoritewikis")
|
||||
case common.Attachment:
|
||||
rows, err = db.Queryx("select attachment_id, wiki_id, modified from attachments")
|
||||
case common.Activity:
|
||||
rows, err = db.Queryx("select activity_id from activities")
|
||||
case common.Lock:
|
||||
rows, err = db.Queryx("select target_id, wiki_id from locks")
|
||||
}
|
||||
} else {
|
||||
// Single objects
|
||||
switch t.(type) {
|
||||
case common.Wiki:
|
||||
re := regexp.MustCompile("/repl/wiki/([0-9a-f-]+)/([0-9-+:.T ]+)")
|
||||
vars := re.FindStringSubmatch(r.URL.Path)
|
||||
modified, terr := time.Parse(time.RFC3339Nano, vars[2])
|
||||
checkError(terr, "Unable to parse wiki time")
|
||||
rows, err = db.Queryx("select wiki_id, title, description, create_user, readacl, writeacl, adminacl, status, modified " +
|
||||
"from wikis " +
|
||||
"where wiki_id=uuid_in($1) and modified=$2", vars[1], modified)
|
||||
case common.Page:
|
||||
re := regexp.MustCompile("/repl/page/([0-9a-f-]+)/([0-9a-f-]+)/([0-9-+:.T ]+)")
|
||||
vars := re.FindStringSubmatch(r.URL.Path)
|
||||
modified, terr := time.Parse(time.RFC3339Nano, vars[3])
|
||||
checkError(terr, "Unable to parse page time")
|
||||
rows, err = db.Queryx("select page_id, wiki_id, path, title, create_user, readacl, writeacl, adminacl, stopinheritation, index, depth, status, modified " +
|
||||
"from pages " +
|
||||
"where page_id=uuid_in($1) and wiki_id=uuid_in($2) and modified=$3", vars[1], vars[2], modified)
|
||||
case common.ContentField:
|
||||
re := regexp.MustCompile("/repl/contentfield/([0-9a-f-]+)/([0-9a-f-]+)/([0-9-+:.T ]+)")
|
||||
vars := re.FindStringSubmatch(r.URL.Path)
|
||||
modified, terr := time.Parse(time.RFC3339Nano, vars[3])
|
||||
checkError(terr, "Unable to parse contentfield time")
|
||||
rows, err = db.Queryx("select contentfield_id, wiki_id, content, modified, status, create_user "+
|
||||
"from contentfields "+
|
||||
"where contentfield_id=uuid_in($1) and wiki_id=uuid_in($2) and modified=$3", vars[1], vars[2], modified)
|
||||
case common.FavoriteWiki:
|
||||
re := regexp.MustCompile("/repl/favoritewiki/([^/]+)/([0-9a-f-]+)/([0-9-+:.T ]+)")
|
||||
vars := re.FindStringSubmatch(r.URL.Path)
|
||||
modified, terr := time.Parse(time.RFC3339Nano, vars[3])
|
||||
checkError(terr, "Unable to parse favoritewiki time")
|
||||
rows, err = db.Queryx("select username, wiki_id, modified, status "+
|
||||
"from favoritewikis "+
|
||||
"where username=$1 and wiki_id=uuid_in($2) and modified=$3", vars[1], vars[2], modified)
|
||||
case common.Attachment:
|
||||
re := regexp.MustCompile("/repl/attachment/([0-9a-f-]+)/([0-9a-f-]+)/([0-9-+:.T ]+)")
|
||||
vars := re.FindStringSubmatch(r.URL.Path)
|
||||
modified, terr := time.Parse(time.RFC3339Nano, vars[3])
|
||||
checkError(terr, "Unable to parse attachment time")
|
||||
rows, err = db.Queryx("select attachment_id, wiki_id, encode(attachment, 'base64') as attachment, mime, filename, create_user, modified, status "+
|
||||
"from attachments "+
|
||||
"where attachment_id=uuid_in($1) and wiki_id=uuid_in($2) and modified=$3", vars[1], vars[2], modified)
|
||||
case common.Activity:
|
||||
re := regexp.MustCompile("/repl/activity/([0-9a-f-]+)")
|
||||
vars := re.FindStringSubmatch(r.URL.Path)
|
||||
rows, err = db.Queryx("select activity_id, timestamp, user_id, user_name, activity_type, target_type, target_title, target_id, readacl, writeacl, adminacl " +
|
||||
"from activities " +
|
||||
"where activity_id=uuid_in($1)", vars[1])
|
||||
case common.Lock:
|
||||
re := regexp.MustCompile("/repl/lock/([0-9a-f-]+)/([0-9a-f-]+)")
|
||||
vars := re.FindStringSubmatch(r.URL.Path)
|
||||
rows, err = db.Queryx("select target_id, wiki_id, username, realname, modified " +
|
||||
"from locks " +
|
||||
"where wiki_id=uuid_in($1) and target_id=($2)", vars[1], vars[2])
|
||||
}
|
||||
}
|
||||
|
||||
checkError(err, "db.Query")
|
||||
|
||||
// Build the JSON
|
||||
var jsontext []byte
|
||||
|
||||
if l {
|
||||
jsontext, err = buildListJSON(rows, t)
|
||||
} else {
|
||||
jsontext, err = buildJSON(rows, t)
|
||||
}
|
||||
|
||||
checkError(err, "json.buildJSON")
|
||||
|
||||
// Print the JSON
|
||||
fmt.Fprintf(w, "%s", jsontext)
|
||||
|
||||
}
|
||||
|
||||
func main() {
|
||||
var settings common.ServiceConfiguration
|
||||
settings.ConfigFrom("server.json")
|
||||
|
||||
// Load my SSL key and certificate
|
||||
cert, err := tls.LoadX509KeyPair(settings.MyCertificateFile, settings.MyKeyFile)
|
||||
checkError(err, "LoadX509KeyPair")
|
||||
|
||||
// Load the CA certificate for client certificate validation
|
||||
capool := x509.NewCertPool()
|
||||
cacert, err := ioutil.ReadFile(settings.CAKeyFile)
|
||||
checkError(err, "loadCACert")
|
||||
capool.AppendCertsFromPEM(cacert)
|
||||
|
||||
// Prepare server configuration
|
||||
config := tls.Config{Certificates: []tls.Certificate{cert}, ClientCAs: capool, ClientAuth: tls.RequireAndVerifyClientCert}
|
||||
config.NextProtos = []string{"http/1.1"}
|
||||
config.Rand = rand.Reader
|
||||
|
||||
// Web server and the handler methods on the paths
|
||||
myTLSWebServer := &http.Server{Addr: settings.ServiceAddress, TLSConfig: &config, Handler: nil}
|
||||
http.HandleFunc("/", myHandler)
|
||||
http.HandleFunc("/repl/wikis", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.Wiki{},true) })
|
||||
http.HandleFunc("/repl/wiki/", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.Wiki{},false) })
|
||||
http.HandleFunc("/repl/pages", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.Page{},true) })
|
||||
http.HandleFunc("/repl/page/", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.Page{},false) })
|
||||
http.HandleFunc("/repl/contentfields", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.ContentField{},true) })
|
||||
http.HandleFunc("/repl/contentfield/", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.ContentField{},false) })
|
||||
http.HandleFunc("/repl/favoritewikis", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.FavoriteWiki{},true) })
|
||||
http.HandleFunc("/repl/favoritewiki/", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.FavoriteWiki{},false) })
|
||||
http.HandleFunc("/repl/attachment/", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.Attachment{},false) })
|
||||
http.HandleFunc("/repl/attachments", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.Attachment{},true) })
|
||||
http.HandleFunc("/repl/activity/", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.Activity{},false) })
|
||||
http.HandleFunc("/repl/activities", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.Activity{},true) })
|
||||
http.HandleFunc("/repl/lock/", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.Lock{},false) })
|
||||
http.HandleFunc("/repl/locks", func(w http.ResponseWriter, r *http.Request) { uniHandler(w,r,common.Lock{},true) })
|
||||
|
||||
// Bind to port
|
||||
conn, err := net.Listen("tcp", settings.ServiceAddress)
|
||||
checkError(err, "Listen")
|
||||
|
||||
// Start the web server and serve until the doomsday
|
||||
tlsListener := tls.NewListener(conn, &config)
|
||||
err = myTLSWebServer.Serve(tlsListener)
|
||||
checkError(err, "Serve")
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"CAKeyFile": "/root/go/src/iw/certificates/ca.crt",
|
||||
"MyCertificateFile": "/root/go/src/iw/certificates/my.crt",
|
||||
"MyKeyFile": "/root/go/src/iw/certificates/my.pem",
|
||||
"ValidPeers": ["dev.localdomain"],
|
||||
"ServiceAddress": "0.0.0.0:8000",
|
||||
"DBUser": "wiki",
|
||||
"DBPassword": "password",
|
||||
"DBName": "wiki"
|
||||
}
|
||||
|
Binary file not shown.
|
@ -0,0 +1,138 @@
|
|||
CKEditor 4 Changelog
|
||||
====================
|
||||
|
||||
## CKEditor 4.1.1
|
||||
|
||||
* Added new translation: Albanian.
|
||||
* [#10172](http://dev.ckeditor.com/ticket/10172): Pressing *Delete*/*Backspace* in an empty table cell moves the cursor to the next/previous cell.
|
||||
* [#10219](http://dev.ckeditor.com/ticket/10219): Error thrown when destroying an editor instance in parallel with a mouseup event.
|
||||
* [#10265](http://dev.ckeditor.com/ticket/10265): Wrong loop type in the Filebrowser plugin.
|
||||
* [#10249](http://dev.ckeditor.com/ticket/10249): Wrong undo/redo states at start.
|
||||
* [#10268](http://dev.ckeditor.com/ticket/10268): "Show Blocks" does not recover after switching to source view.
|
||||
* [#9995](http://dev.ckeditor.com/ticket/9995): HTML code in `textarea` should not be modified by the `htmlDataProcessor`.
|
||||
* [#10320](http://dev.ckeditor.com/ticket/10320): Justify plugin should add elements to the ACF based on current Enter mode.
|
||||
* [#10260](http://dev.ckeditor.com/ticket/10260): Fixed: Advanced Content Filter blocks `tabSpaces`. Unified `data-cke-*` attributes filtering.
|
||||
* [#10315](http://dev.ckeditor.com/ticket/10315): [Webkit] Undo manager should not record snapshots after a filling character was added/removed.
|
||||
* [#10291](http://dev.ckeditor.com/ticket/10291): [Webkit] Space after a filling character should be secured.
|
||||
* [#10330](http://dev.ckeditor.com/ticket/10330): [Webkit] The filling character is not removed on `keydown` in specific cases.
|
||||
* [#10285](http://dev.ckeditor.com/ticket/10285): Fixed: Styled text pasted from MS Word causes an infinite loop.
|
||||
* [#10131](http://dev.ckeditor.com/ticket/10131): Fixed: `undoManager#update` does not refresh the command state.
|
||||
* [#10337](http://dev.ckeditor.com/ticket/10337): Fixed: Unable to remove `<s>` using `removeformat`.
|
||||
|
||||
## CKEditor 4.1
|
||||
|
||||
* [#10192](http://dev.ckeditor.com/ticket/10192): Closing lists with Enter key does not work with Advanced Content Filter in several cases.
|
||||
* [#10191](http://dev.ckeditor.com/ticket/10191): Fixed allowed content rules unification, so the `filter.allowedContent` property always contains rules in the same format.
|
||||
* [#10224](http://dev.ckeditor.com/ticket/10224): Advanced Content Filter does not remove non-empty `<a>` elements anymore.
|
||||
* Minor issues in plugin integration with Advanced Content Filter:
|
||||
* [#10166](http://dev.ckeditor.com/ticket/10166): Added transformation from the `align` attribute to `float` style to preserve backward compatibility after the introduction of Advanced Content Filter.
|
||||
* [#10195](http://dev.ckeditor.com/ticket/10195): Image plugin no longer registers rules for links to Advanced Content Filter.
|
||||
* [#10213](http://dev.ckeditor.com/ticket/10213): Justify plugin is now correctly registering rules to Advanced Content Filter when `config.justifyClasses` is defined.
|
||||
|
||||
## CKEditor 4.1 RC
|
||||
|
||||
* [#9829](http://dev.ckeditor.com/ticket/9829): Data and features activation based on editor configuration.
|
||||
|
||||
Brand new data filtering system that works in 2 modes:
|
||||
|
||||
* based on loaded features (toolbar items, plugins) - the data will be filtered according to what the editor in its
|
||||
current configuration can handle,
|
||||
* based on `config.allowedContent` rules - the data will be filtered and the editor features (toolbar items, commands,
|
||||
keystrokes) will be enabled if they are allowed.
|
||||
|
||||
See the `datafiltering.html` sample, [guides](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter) and [`CKEDITOR.filter` API documentation](http://docs.ckeditor.com/#!/api/CKEDITOR.filter).
|
||||
* [#9387](http://dev.ckeditor.com/ticket/9387): Reintroduced "Shared Spaces" - the ability to display toolbar and bottom editor space in selected locations and to share them by different editor instances.
|
||||
* [#9907](http://dev.ckeditor.com/ticket/9907): Added the `contentPreview` event for preview data manipulation.
|
||||
* [#9713](http://dev.ckeditor.com/ticket/9713): Introduced the `sourcedialog` plugin that brings raw HTML editing for inline editor instances.
|
||||
* Included in [#9829](http://dev.ckeditor.com/ticket/9829): Introduced new events, `toHtml` and `toDataFormat`, allowing for better integration with data processing. See API documentation: [`toHtml`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-toHtml), [`toDataFormat`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-toDataFormat).
|
||||
* [#9981](http://dev.ckeditor.com/ticket/9981): Added ability to filter `htmlParser.fragment`, `htmlParser.element` etc. by many `htmlParser.filter`s before writing structure to an HTML string.
|
||||
* Included in [#10103](http://dev.ckeditor.com/ticket/10103):
|
||||
* Introduced the `editor.status` property to make it easier to check the current status of the editor. See [API documentation](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-property-status).
|
||||
* Default `command` state is now `CKEDITOR.TRISTATE_DISABLE`. It will be activated on `editor.instanceReady` or immediately after being added if the editor is already initialized.
|
||||
* [#9796](http://dev.ckeditor.com/ticket/9796): Introduced `<s>` as a default tag for strikethrough, which replaces obsolete `<strike>` in HTML5.
|
||||
|
||||
## CKEditor 4.0.3
|
||||
|
||||
* [#10196](http://dev.ckeditor.com/ticket/10196): Fixed context menus not opening with keyboard shortcuts when Autogrow is enabled.
|
||||
* [#10212](http://dev.ckeditor.com/ticket/10212): [IE7-10] Undo command throws errors after multiple switches between Source and WYSIWYG view.
|
||||
* [#10219](http://dev.ckeditor.com/ticket/10219): [Inline editor] Error thrown after calling editor.destroy().
|
||||
|
||||
## CKEditor 4.0.2
|
||||
|
||||
* [#9779](http://dev.ckeditor.com/ticket/9779): Fixed overriding `CKEDITOR.getUrl` with `CKEDITOR_GETURL`.
|
||||
* [#9772](http://dev.ckeditor.com/ticket/9772): Custom buttons in dialog window footer have different look and size (Moono, Kama).
|
||||
* [#9029](http://dev.ckeditor.com/ticket/9029): Custom styles added with `styleSet.add()` are displayed in wrong order.
|
||||
* [#9887](http://dev.ckeditor.com/ticket/9887): Disable magicline when `editor.readOnly` is set.
|
||||
* [#9882](http://dev.ckeditor.com/ticket/9882): Fixed empty document title on `getData()` if set via the Document Properties dialog window.
|
||||
* [#9773](http://dev.ckeditor.com/ticket/9773): Fixed rendering problems with selection fields in the Kama skin.
|
||||
* [#9851](http://dev.ckeditor.com/ticket/9851): The `selectionChange` event is not fired when mouse selection ended outside editable.
|
||||
* [#9903](http://dev.ckeditor.com/ticket/9903): [Inline editor] Bad positioning of floating space with page horizontal scroll.
|
||||
* [#9872](http://dev.ckeditor.com/ticket/9872): `editor.checkDirty()` returns `true` when called onload. Removed the obsolete `editor.mayBeDirty` flag.
|
||||
* [#9893](http://dev.ckeditor.com/ticket/9893): Fixed broken toolbar when editing mixed direction content in Quirks mode.
|
||||
* [#9845](http://dev.ckeditor.com/ticket/9845): Fixed TAB navigation in the Link dialog window when the Anchor option is used and no anchors are available.
|
||||
* [#9883](http://dev.ckeditor.com/ticket/9883): Maximizing was making the entire page editable with divarea-based editors.
|
||||
* [#9940](http://dev.ckeditor.com/ticket/9940): [Firefox] Navigating back to a page with the editor was making the entire page editable.
|
||||
* [#9966](http://dev.ckeditor.com/ticket/9966): Fixed: Unable to type square brackets with French keyboard layout. Changed magicline keystrokes.
|
||||
* [#9507](http://dev.ckeditor.com/ticket/9507): [Firefox] Selection is moved before editable position when the editor is focused for the first time.
|
||||
* [#9947](http://dev.ckeditor.com/ticket/9947): [Webkit] Editor overflows parent container in some edge cases.
|
||||
* [#10105](http://dev.ckeditor.com/ticket/10105): Fixed: Broken sourcearea view when an RTL language is set.
|
||||
* [#10123](http://dev.ckeditor.com/ticket/10123): [Webkit] Fixed: Several dialog windows have broken layout since the latest Webkit release.
|
||||
* [#10152](http://dev.ckeditor.com/ticket/10152): Fixed: Invalid ARIA property used on menu items.
|
||||
|
||||
## CKEditor 4.0.1.1
|
||||
|
||||
* Security update: Added protection against XSS attack and possible path disclosure in PHP sample.
|
||||
|
||||
## CKEditor 4.0.1
|
||||
|
||||
Fixed issues:
|
||||
|
||||
* [#9655](http://dev.ckeditor.com/ticket/9655): Support for IE Quirks Mode in new Moono skin.
|
||||
* Accessibility issues (mainly on inline editor): [#9364](http://dev.ckeditor.com/ticket/9364), [#9368](http://dev.ckeditor.com/ticket/9368), [#9369](http://dev.ckeditor.com/ticket/9369), [#9370](http://dev.ckeditor.com/ticket/9370), [#9541](http://dev.ckeditor.com/ticket/9541), [#9543](http://dev.ckeditor.com/ticket/9543), [#9841](http://dev.ckeditor.com/ticket/9841), [#9844](http://dev.ckeditor.com/ticket/9844).
|
||||
* Magic-line:
|
||||
* [#9481](http://dev.ckeditor.com/ticket/9481): Added accessibility support for Magic-line.
|
||||
* [#9509](http://dev.ckeditor.com/ticket/9509): Added Magic-line support for forms.
|
||||
* [#9573](http://dev.ckeditor.com/ticket/9573): Magic-line doesn't disappear on `mouseout` in the specific case.
|
||||
* [#9754](http://dev.ckeditor.com/ticket/9754): [Webkit] Cut & paste simple unformatted text generates inline wrapper in Webkits.
|
||||
* [#9456](http://dev.ckeditor.com/ticket/9456): [Chrome] Properly paste bullet list style from MS-Word.
|
||||
* [#9699](http://dev.ckeditor.com/ticket/9699), [#9758](http://dev.ckeditor.com/ticket/9758): Improved selection locking when selecting by dragging.
|
||||
* Context menu:
|
||||
* [#9712](http://dev.ckeditor.com/ticket/9712): Context menu open destroys editor focus.
|
||||
* [#9366](http://dev.ckeditor.com/ticket/9366): Context menu should be displayed over floating toolbar.
|
||||
* [#9706](http://dev.ckeditor.com/ticket/9706): Context menu generates JS error in inline mode when editor attached to header element.
|
||||
* [#9800](http://dev.ckeditor.com/ticket/9800): Hide float panel when resizing window.
|
||||
* [#9721](http://dev.ckeditor.com/ticket/9721): Padding in content of div based editor puts editing area under bottom UI space.
|
||||
* [#9528](http://dev.ckeditor.com/ticket/9528): Host page's `box-sizing` style shouldn't influence editor UI elements.
|
||||
* [#9503](http://dev.ckeditor.com/ticket/9503): Forms plugin adds context menu listeners only on supported input types. Added support for `tel, email, search` and `url` input types.
|
||||
* [#9769](http://dev.ckeditor.com/ticket/9769): Improved floating toolbar positioning in narrow window.
|
||||
* [#9875](http://dev.ckeditor.com/ticket/9875): Table dialog doesn't populate width correctly.
|
||||
* [#8675](http://dev.ckeditor.com/ticket/8675): Deleting cells in nested table removes outer table cell.
|
||||
* [#9815](http://dev.ckeditor.com/ticket/9815): Can't edit dialog fields on editor initialized in jQuery UI modal dialog.
|
||||
* [#8888](http://dev.ckeditor.com/ticket/8888): CKEditor dialogs do not show completely in small window.
|
||||
* [#9360](http://dev.ckeditor.com/ticket/9360): [Inline editor] Blocks shown for a div stay permanently even after user exists editing the div.
|
||||
* [#9531](http://dev.ckeditor.com/ticket/9531): [Firefox & Inline editor] Toolbar is lost when closing format combo by clicking on its button.
|
||||
* [#9553](http://dev.ckeditor.com/ticket/9553): Table width incorrectly set when `border-width` style is specified.
|
||||
* [#9594](http://dev.ckeditor.com/ticket/9594): Cannot tab past CKEditor when it is in read only mode.
|
||||
* [#9658](http://dev.ckeditor.com/ticket/9658): [IE9] Justify not working on selected image.
|
||||
* [#9686](http://dev.ckeditor.com/ticket/9686): Added missing contents styles for `<pre>`.
|
||||
* [#9709](http://dev.ckeditor.com/ticket/9709): PasteFromWord should not depend on configuration from other styles.
|
||||
* [#9726](http://dev.ckeditor.com/ticket/9726): Removed color dialog dependency from table tools.
|
||||
* [#9765](http://dev.ckeditor.com/ticket/9765): Toolbar Collapse command documented incorrectly on Accessibility Instructions dialog.
|
||||
* [#9771](http://dev.ckeditor.com/ticket/9771): [Webkit & Opera] Fixed scrolling issues when pasting.
|
||||
* [#9787](http://dev.ckeditor.com/ticket/9787): [IE9] onChange isn't fired for checkboxes in dialogs.
|
||||
* [#9842](http://dev.ckeditor.com/ticket/9842): [Firefox 17] When we open toolbar menu for the first time & press down arrow key, focus goes to next toolbar button instead of menu options.
|
||||
* [#9847](http://dev.ckeditor.com/ticket/9847): Elements path shouldn't be initialized on inline editor.
|
||||
* [#9853](http://dev.ckeditor.com/ticket/9853): `Editor#addRemoveFormatFilter` is exposed before it really works.
|
||||
* [#8893](http://dev.ckeditor.com/ticket/8893): Value of `pasteFromWordCleanupFile` config is now taken from instance configuration.
|
||||
* [#9693](http://dev.ckeditor.com/ticket/9693): Removed "live preview" checkbox from UI color picker.
|
||||
|
||||
|
||||
## CKEditor 4.0
|
||||
|
||||
The first stable release of the new CKEditor 4 code line.
|
||||
|
||||
The CKEditor JavaScript API has been kept compatible with CKEditor 4, whenever
|
||||
possible. The list of relevant changes can be found in the [API Changes page of
|
||||
the CKEditor 4 documentation][1].
|
||||
|
||||
[1]: http://docs.ckeditor.com/#!/guide/dev_api_changes "API Changes"
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,39 @@
|
|||
CKEditor 4
|
||||
==========
|
||||
|
||||
Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.
|
||||
http://ckeditor.com - See LICENSE.md for license information.
|
||||
|
||||
CKEditor is a text editor to be used inside web pages. It's not a replacement
|
||||
for desktop text editors like Word or OpenOffice, but a component to be used as
|
||||
part of web applications and websites.
|
||||
|
||||
## Documentation
|
||||
|
||||
The full editor documentation is available online at the following address:
|
||||
http://docs.ckeditor.com
|
||||
|
||||
## Installation
|
||||
|
||||
Installing CKEditor is an easy task. Just follow these simple steps:
|
||||
|
||||
1. **Download** the latest version from the CKEditor website:
|
||||
http://ckeditor.com. You should have already completed this step, but be
|
||||
sure you have the very latest version.
|
||||
2. **Extract** (decompress) the downloaded file into the root of your website.
|
||||
|
||||
**Note:** CKEditor is by default installed in the `ckeditor` folder. You can
|
||||
place the files in whichever you want though.
|
||||
|
||||
## Checking Your Installation
|
||||
|
||||
The editor comes with a few sample pages that can be used to verify that
|
||||
installation proceeded properly. Take a look at the `samples` directory.
|
||||
|
||||
To test your installation, just call the following page at your website:
|
||||
|
||||
http://<your site>/<CKEditor installation path>/samples/index.html
|
||||
|
||||
For example:
|
||||
|
||||
http://www.example.com/ckeditor/samples/index.html
|
|
@ -0,0 +1,139 @@
|
|||
|
||||
/**
|
||||
* @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.html or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file was added automatically by CKEditor builder.
|
||||
* You may re-use it at any time at http://ckeditor.com/builder to build CKEditor again.
|
||||
*
|
||||
* NOTE:
|
||||
* This file is not used by CKEditor, you may remove it.
|
||||
* Changing this file will not change your CKEditor configuration.
|
||||
*/
|
||||
|
||||
var CKBUILDER_CONFIG = {
|
||||
skin: 'moono',
|
||||
preset: 'standard',
|
||||
ignore: [
|
||||
'dev',
|
||||
'.gitignore',
|
||||
'.gitattributes',
|
||||
'README.md',
|
||||
'.mailmap'
|
||||
],
|
||||
plugins : {
|
||||
'about' : 1,
|
||||
'a11yhelp' : 1,
|
||||
'basicstyles' : 1,
|
||||
'blockquote' : 1,
|
||||
'clipboard' : 1,
|
||||
'contextmenu' : 1,
|
||||
'resize' : 1,
|
||||
'toolbar' : 1,
|
||||
'elementspath' : 1,
|
||||
'enterkey' : 1,
|
||||
'entities' : 1,
|
||||
'filebrowser' : 1,
|
||||
'floatingspace' : 1,
|
||||
'format' : 1,
|
||||
'htmlwriter' : 1,
|
||||
'horizontalrule' : 1,
|
||||
'wysiwygarea' : 1,
|
||||
'image' : 1,
|
||||
'indent' : 1,
|
||||
'link' : 1,
|
||||
'list' : 1,
|
||||
'magicline' : 1,
|
||||
'maximize' : 1,
|
||||
'pastetext' : 1,
|
||||
'pastefromword' : 1,
|
||||
'removeformat' : 1,
|
||||
'sourcearea' : 1,
|
||||
'specialchar' : 1,
|
||||
'scayt' : 1,
|
||||
'stylescombo' : 1,
|
||||
'tab' : 1,
|
||||
'table' : 1,
|
||||
'tabletools' : 1,
|
||||
'undo' : 1,
|
||||
'wsc' : 1,
|
||||
'dialog' : 1,
|
||||
'dialogui' : 1,
|
||||
'menu' : 1,
|
||||
'floatpanel' : 1,
|
||||
'panel' : 1,
|
||||
'button' : 1,
|
||||
'popup' : 1,
|
||||
'richcombo' : 1,
|
||||
'listblock' : 1,
|
||||
'fakeobjects' : 1,
|
||||
'menubutton' : 1
|
||||
},
|
||||
languages : {
|
||||
'af' : 1,
|
||||
'sq' : 1,
|
||||
'ar' : 1,
|
||||
'eu' : 1,
|
||||
'bn' : 1,
|
||||
'bs' : 1,
|
||||
'bg' : 1,
|
||||
'ca' : 1,
|
||||
'zh-cn' : 1,
|
||||
'zh' : 1,
|
||||
'hr' : 1,
|
||||
'cs' : 1,
|
||||
'da' : 1,
|
||||
'nl' : 1,
|
||||
'en' : 1,
|
||||
'en-au' : 1,
|
||||
'en-ca' : 1,
|
||||
'en-gb' : 1,
|
||||
'eo' : 1,
|
||||
'et' : 1,
|
||||
'fo' : 1,
|
||||
'fi' : 1,
|
||||
'fr' : 1,
|
||||
'fr-ca' : 1,
|
||||
'gl' : 1,
|
||||
'ka' : 1,
|
||||
'de' : 1,
|
||||
'el' : 1,
|
||||
'gu' : 1,
|
||||
'he' : 1,
|
||||
'hi' : 1,
|
||||
'hu' : 1,
|
||||
'is' : 1,
|
||||
'it' : 1,
|
||||
'ja' : 1,
|
||||
'km' : 1,
|
||||
'ko' : 1,
|
||||
'ku' : 1,
|
||||
'lv' : 1,
|
||||
'lt' : 1,
|
||||
'mk' : 1,
|
||||
'ms' : 1,
|
||||
'mn' : 1,
|
||||
'no' : 1,
|
||||
'nb' : 1,
|
||||
'fa' : 1,
|
||||
'pl' : 1,
|
||||
'pt-br' : 1,
|
||||
'pt' : 1,
|
||||
'ro' : 1,
|
||||
'ru' : 1,
|
||||
'sr' : 1,
|
||||
'sr-latn' : 1,
|
||||
'sk' : 1,
|
||||
'sl' : 1,
|
||||
'es' : 1,
|
||||
'sv' : 1,
|
||||
'th' : 1,
|
||||
'tr' : 1,
|
||||
'ug' : 1,
|
||||
'uk' : 1,
|
||||
'vi' : 1,
|
||||
'cy' : 1,
|
||||
}
|
||||
};
|
|
@ -0,0 +1,851 @@
|
|||
/*
|
||||
Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved.
|
||||
For licensing, see LICENSE.html or http://ckeditor.com/license
|
||||
*/
|
||||
(function(){if(window.CKEDITOR&&window.CKEDITOR.dom)return;window.CKEDITOR||(window.CKEDITOR=function(){var a={timestamp:"D3NA",version:"4.1.1",revision:"5a2a7e3",rnd:Math.floor(900*Math.random())+100,_:{pending:[]},status:"unloaded",basePath:function(){var b=window.CKEDITOR_BASEPATH||"";if(!b)for(var a=document.getElementsByTagName("script"),d=0;d<a.length;d++){var c=a[d].src.match(/(^|.*[\\\/])ckeditor(?:_basic)?(?:_source)?.js(?:\?.*)?$/i);if(c){b=c[1];break}}-1==b.indexOf(":/")&&(b=0===b.indexOf("/")?location.href.match(/^.*?:\/\/[^\/]*/)[0]+b:location.href.match(/^[^\?]*\/(?:)/)[0]+
|
||||
b);if(!b)throw'The CKEditor installation path could not be automatically detected. Please set the global variable "CKEDITOR_BASEPATH" before creating editor instances.';return b}(),getUrl:function(b){-1==b.indexOf(":/")&&0!==b.indexOf("/")&&(b=this.basePath+b);this.timestamp&&("/"!=b.charAt(b.length-1)&&!/[&?]t=/.test(b))&&(b+=(0<=b.indexOf("?")?"&":"?")+"t="+this.timestamp);return b},domReady:function(){function b(){try{document.addEventListener?(document.removeEventListener("DOMContentLoaded",b,
|
||||
!1),a()):document.attachEvent&&"complete"===document.readyState&&(document.detachEvent("onreadystatechange",b),a())}catch(d){}}function a(){for(var b;b=d.shift();)b()}var d=[];return function(a){d.push(a);"complete"===document.readyState&&setTimeout(b,1);if(1==d.length)if(document.addEventListener)document.addEventListener("DOMContentLoaded",b,!1),window.addEventListener("load",b,!1);else if(document.attachEvent){document.attachEvent("onreadystatechange",b);window.attachEvent("onload",b);a=!1;try{a=
|
||||
!window.frameElement}catch(e){}if(document.documentElement.doScroll&&a){var c=function(){try{document.documentElement.doScroll("left")}catch(a){setTimeout(c,1);return}b()};c()}}}}()},c=window.CKEDITOR_GETURL;if(c){var b=a.getUrl;a.getUrl=function(f){return c.call(a,f)||b.call(a,f)}}return a}());
|
||||
CKEDITOR.event||(CKEDITOR.event=function(){},CKEDITOR.event.implementOn=function(a){var c=CKEDITOR.event.prototype,b;for(b in c)a[b]==void 0&&(a[b]=c[b])},CKEDITOR.event.prototype=function(){function a(f){var a=c(this);return a[f]||(a[f]=new b(f))}var c=function(b){b=b.getPrivate&&b.getPrivate()||b._||(b._={});return b.events||(b.events={})},b=function(b){this.name=b;this.listeners=[]};b.prototype={getListenerIndex:function(b){for(var a=0,d=this.listeners;a<d.length;a++)if(d[a].fn==b)return a;return-1}};
|
||||
return{define:function(b,e){var d=a.call(this,b);CKEDITOR.tools.extend(d,e,true)},on:function(b,e,d,c,l){function i(a,h,n,p){a={name:b,sender:this,editor:a,data:h,listenerData:c,stop:n,cancel:p,removeListener:m};return e.call(d,a)===false?false:a.data}function m(){r.removeListener(b,e)}var n=a.call(this,b);if(n.getListenerIndex(e)<0){n=n.listeners;d||(d=this);isNaN(l)&&(l=10);var r=this;i.fn=e;i.priority=l;for(var p=n.length-1;p>=0;p--)if(n[p].priority<=l){n.splice(p+1,0,i);return{removeListener:m}}n.unshift(i)}return{removeListener:m}},
|
||||
once:function(){var b=arguments[1];arguments[1]=function(a){a.removeListener();return b.apply(this,arguments)};return this.on.apply(this,arguments)},capture:function(){CKEDITOR.event.useCapture=1;var b=this.on.apply(this,arguments);CKEDITOR.event.useCapture=0;return b},fire:function(){var b=0,a=function(){b=1},d=0,j=function(){d=1};return function(l,i,m){var n=c(this)[l],l=b,r=d;b=d=0;if(n){var p=n.listeners;if(p.length)for(var p=p.slice(0),g,h=0;h<p.length;h++){if(n.errorProof)try{g=p[h].call(this,
|
||||
m,i,a,j)}catch(u){}else g=p[h].call(this,m,i,a,j);g===false?d=1:typeof g!="undefined"&&(i=g);if(b||d)break}}i=d?false:typeof i=="undefined"?true:i;b=l;d=r;return i}}(),fireOnce:function(b,a,d){a=this.fire(b,a,d);delete c(this)[b];return a},removeListener:function(b,a){var d=c(this)[b];if(d){var j=d.getListenerIndex(a);j>=0&&d.listeners.splice(j,1)}},removeAllListeners:function(){var b=c(this),a;for(a in b)delete b[a]},hasListeners:function(b){return(b=c(this)[b])&&b.listeners.length>0}}}());
|
||||
CKEDITOR.editor||(CKEDITOR.editor=function(){CKEDITOR._.pending.push([this,arguments]);CKEDITOR.event.call(this)},CKEDITOR.editor.prototype.fire=function(a,c){a in{instanceReady:1,loaded:1}&&(this[a]=true);return CKEDITOR.event.prototype.fire.call(this,a,c,this)},CKEDITOR.editor.prototype.fireOnce=function(a,c){a in{instanceReady:1,loaded:1}&&(this[a]=true);return CKEDITOR.event.prototype.fireOnce.call(this,a,c,this)},CKEDITOR.event.implementOn(CKEDITOR.editor.prototype));
|
||||
CKEDITOR.env||(CKEDITOR.env=function(){var a=navigator.userAgent.toLowerCase(),c=window.opera,b={ie:eval("/*@cc_on!@*/false"),opera:!!c&&c.version,webkit:a.indexOf(" applewebkit/")>-1,air:a.indexOf(" adobeair/")>-1,mac:a.indexOf("macintosh")>-1,quirks:document.compatMode=="BackCompat",mobile:a.indexOf("mobile")>-1,iOS:/(ipad|iphone|ipod)/.test(a),isCustomDomain:function(){if(!this.ie)return false;var b=document.domain,a=window.location.hostname;return b!=a&&b!="["+a+"]"},secure:location.protocol==
|
||||
"https:"};b.gecko=navigator.product=="Gecko"&&!b.webkit&&!b.opera;if(b.webkit)a.indexOf("chrome")>-1?b.chrome=true:b.safari=true;var f=0;if(b.ie){f=b.quirks||!document.documentMode?parseFloat(a.match(/msie (\d+)/)[1]):document.documentMode;b.ie9Compat=f==9;b.ie8Compat=f==8;b.ie7Compat=f==7;b.ie6Compat=f<7||b.quirks}if(b.gecko){var e=a.match(/rv:([\d\.]+)/);if(e){e=e[1].split(".");f=e[0]*1E4+(e[1]||0)*100+(e[2]||0)*1}}b.opera&&(f=parseFloat(c.version()));b.air&&(f=parseFloat(a.match(/ adobeair\/(\d+)/)[1]));
|
||||
b.webkit&&(f=parseFloat(a.match(/ applewebkit\/(\d+)/)[1]));b.version=f;b.isCompatible=b.iOS&&f>=534||!b.mobile&&(b.ie&&f>6||b.gecko&&f>=10801||b.opera&&f>=9.5||b.air&&f>=1||b.webkit&&f>=522||false);b.cssClass="cke_browser_"+(b.ie?"ie":b.gecko?"gecko":b.opera?"opera":b.webkit?"webkit":"unknown");if(b.quirks)b.cssClass=b.cssClass+" cke_browser_quirks";if(b.ie){b.cssClass=b.cssClass+(" cke_browser_ie"+(b.quirks||b.version<7?"6":b.version));if(b.quirks)b.cssClass=b.cssClass+" cke_browser_iequirks"}if(b.gecko)if(f<
|
||||
10900)b.cssClass=b.cssClass+" cke_browser_gecko18";else if(f<=11E3)b.cssClass=b.cssClass+" cke_browser_gecko19";if(b.air)b.cssClass=b.cssClass+" cke_browser_air";return b}());
|
||||
"unloaded"==CKEDITOR.status&&function(){CKEDITOR.event.implementOn(CKEDITOR);CKEDITOR.loadFullCore=function(){if(CKEDITOR.status!="basic_ready")CKEDITOR.loadFullCore._load=1;else{delete CKEDITOR.loadFullCore;var a=document.createElement("script");a.type="text/javascript";a.src=CKEDITOR.basePath+"ckeditor.js";document.getElementsByTagName("head")[0].appendChild(a)}};CKEDITOR.loadFullCoreTimeout=0;CKEDITOR.add=function(a){(this._.pending||(this._.pending=[])).push(a)};(function(){CKEDITOR.domReady(function(){var a=
|
||||
CKEDITOR.loadFullCore,c=CKEDITOR.loadFullCoreTimeout;if(a){CKEDITOR.status="basic_ready";a&&a._load?a():c&&setTimeout(function(){CKEDITOR.loadFullCore&&CKEDITOR.loadFullCore()},c*1E3)}})})();CKEDITOR.status="basic_loaded"}();CKEDITOR.dom={};
|
||||
(function(){var a=[],c=CKEDITOR.env.gecko?"-moz-":CKEDITOR.env.webkit?"-webkit-":CKEDITOR.env.opera?"-o-":CKEDITOR.env.ie?"-ms-":"";CKEDITOR.on("reset",function(){a=[]});CKEDITOR.tools={arrayCompare:function(b,a){if(!b&&!a)return true;if(!b||!a||b.length!=a.length)return false;for(var e=0;e<b.length;e++)if(b[e]!=a[e])return false;return true},clone:function(b){var a;if(b&&b instanceof Array){a=[];for(var e=0;e<b.length;e++)a[e]=CKEDITOR.tools.clone(b[e]);return a}if(b===null||typeof b!="object"||
|
||||
b instanceof String||b instanceof Number||b instanceof Boolean||b instanceof Date||b instanceof RegExp)return b;a=new b.constructor;for(e in b)a[e]=CKEDITOR.tools.clone(b[e]);return a},capitalize:function(b){return b.charAt(0).toUpperCase()+b.substring(1).toLowerCase()},extend:function(b){var a=arguments.length,e,d;if(typeof(e=arguments[a-1])=="boolean")a--;else if(typeof(e=arguments[a-2])=="boolean"){d=arguments[a-1];a=a-2}for(var c=1;c<a;c++){var l=arguments[c],i;for(i in l)if(e===true||b[i]==void 0)if(!d||
|
||||
i in d)b[i]=l[i]}return b},prototypedCopy:function(b){var a=function(){};a.prototype=b;return new a},copy:function(b){var a={},e;for(e in b)a[e]=b[e];return a},isArray:function(b){return!!b&&b instanceof Array},isEmpty:function(b){for(var a in b)if(b.hasOwnProperty(a))return false;return true},cssVendorPrefix:function(b,a,e){if(e)return c+b+":"+a+";"+b+":"+a;e={};e[b]=a;e[c+b]=a;return e},cssStyleToDomStyle:function(){var b=document.createElement("div").style,a=typeof b.cssFloat!="undefined"?"cssFloat":
|
||||
typeof b.styleFloat!="undefined"?"styleFloat":"float";return function(b){return b=="float"?a:b.replace(/-./g,function(b){return b.substr(1).toUpperCase()})}}(),buildStyleHtml:function(b){for(var b=[].concat(b),a,e=[],d=0;d<b.length;d++)if(a=b[d])/@import|[{}]/.test(a)?e.push("<style>"+a+"</style>"):e.push('<link type="text/css" rel=stylesheet href="'+a+'">');return e.join("")},htmlEncode:function(b){return(""+b).replace(/&/g,"&").replace(/>/g,">").replace(/</g,"<")},htmlEncodeAttr:function(b){return b.replace(/"/g,
|
||||
""").replace(/</g,"<").replace(/>/g,">")},getNextNumber:function(){var b=0;return function(){return++b}}(),getNextId:function(){return"cke_"+this.getNextNumber()},override:function(b,a){var e=a(b);e.prototype=b.prototype;return e},setTimeout:function(b,a,e,d,c){c||(c=window);e||(e=c);return c.setTimeout(function(){d?b.apply(e,[].concat(d)):b.apply(e)},a||0)},trim:function(){var b=/(?:^[ \t\n\r]+)|(?:[ \t\n\r]+$)/g;return function(a){return a.replace(b,"")}}(),ltrim:function(){var b=/^[ \t\n\r]+/g;
|
||||
return function(a){return a.replace(b,"")}}(),rtrim:function(){var b=/[ \t\n\r]+$/g;return function(a){return a.replace(b,"")}}(),indexOf:function(b,a){if(typeof a=="function")for(var e=0,d=b.length;e<d;e++){if(a(b[e]))return e}else{if(b.indexOf)return b.indexOf(a);e=0;for(d=b.length;e<d;e++)if(b[e]===a)return e}return-1},search:function(b,a){var e=CKEDITOR.tools.indexOf(b,a);return e>=0?b[e]:null},bind:function(b,a){return function(){return b.apply(a,arguments)}},createClass:function(b){var a=b.$,
|
||||
e=b.base,d=b.privates||b._,c=b.proto,b=b.statics;!a&&(a=function(){e&&this.base.apply(this,arguments)});if(d)var l=a,a=function(){var b=this._||(this._={}),a;for(a in d){var f=d[a];b[a]=typeof f=="function"?CKEDITOR.tools.bind(f,this):f}l.apply(this,arguments)};if(e){a.prototype=this.prototypedCopy(e.prototype);a.prototype.constructor=a;a.base=e;a.baseProto=e.prototype;a.prototype.base=function(){this.base=e.prototype.base;e.apply(this,arguments);this.base=arguments.callee}}c&&this.extend(a.prototype,
|
||||
c,true);b&&this.extend(a,b,true);return a},addFunction:function(b,f){return a.push(function(){return b.apply(f||this,arguments)})-1},removeFunction:function(b){a[b]=null},callFunction:function(b){var f=a[b];return f&&f.apply(window,Array.prototype.slice.call(arguments,1))},cssLength:function(){var b=/^-?\d+\.?\d*px$/,a;return function(e){a=CKEDITOR.tools.trim(e+"")+"px";return b.test(a)?a:e||""}}(),convertToPx:function(){var b;return function(a){if(!b){b=CKEDITOR.dom.element.createFromHtml('<div style="position:absolute;left:-9999px;top:-9999px;margin:0px;padding:0px;border:0px;"></div>',
|
||||
CKEDITOR.document);CKEDITOR.document.getBody().append(b)}if(!/%$/.test(a)){b.setStyle("width",a);return b.$.clientWidth}return a}}(),repeat:function(b,a){return Array(a+1).join(b)},tryThese:function(){for(var b,a=0,e=arguments.length;a<e;a++){var d=arguments[a];try{b=d();break}catch(c){}}return b},genKey:function(){return Array.prototype.slice.call(arguments).join("-")},defer:function(b){return function(){var a=arguments,e=this;window.setTimeout(function(){b.apply(e,a)},0)}},normalizeCssText:function(b,
|
||||
a){var e=[],d,c=CKEDITOR.tools.parseCssText(b,true,a);for(d in c)e.push(d+":"+c[d]);e.sort();return e.length?e.join(";")+";":""},convertRgbToHex:function(b){return b.replace(/(?:rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\))/gi,function(b,a,d,c){b=[a,d,c];for(a=0;a<3;a++)b[a]=("0"+parseInt(b[a],10).toString(16)).slice(-2);return"#"+b.join("")})},parseCssText:function(b,a,e){var d={};if(e){e=new CKEDITOR.dom.element("span");e.setAttribute("style",b);b=CKEDITOR.tools.convertRgbToHex(e.getAttribute("style")||
|
||||
"")}if(!b||b==";")return d;b.replace(/"/g,'"').replace(/\s*([^:;\s]+)\s*:\s*([^;]+)\s*(?=;|$)/g,function(b,e,c){if(a){e=e.toLowerCase();e=="font-family"&&(c=c.toLowerCase().replace(/["']/g,"").replace(/\s*,\s*/g,","));c=CKEDITOR.tools.trim(c)}d[e]=c});return d},writeCssText:function(b,a){var e,c=[];for(e in b)c.push(e+":"+b[e]);a&&c.sort();return c.join("; ")},objectCompare:function(b,a,c){var d;if(!b&&!a)return true;if(!b||!a)return false;for(d in b)if(b[d]!=a[d])return false;if(!c)for(d in a)if(b[d]!=
|
||||
a[d])return false;return true},objectKeys:function(b){var a=[],c;for(c in b)a.push(c);return a},convertArrayToObject:function(b,a){var c={};arguments.length==1&&(a=true);for(var d=0,j=b.length;d<j;++d)c[b[d]]=a;return c}}})();
|
||||
CKEDITOR.dtd=function(){var a=CKEDITOR.tools.extend,c=function(b,a){for(var f=CKEDITOR.tools.clone(b),c=1;c<arguments.length;c++){var a=arguments[c],g;for(g in a)delete f[g]}return f},b={},f={},e={address:1,article:1,aside:1,blockquote:1,details:1,div:1,dl:1,fieldset:1,figure:1,footer:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,header:1,hgroup:1,hr:1,menu:1,nav:1,ol:1,p:1,pre:1,section:1,table:1,ul:1},d={command:1,link:1,meta:1,noscript:1,script:1,style:1},j={},l={"#":1},i={center:1,dir:1,noframes:1};
|
||||
a(b,{a:1,abbr:1,area:1,audio:1,b:1,bdi:1,bdo:1,br:1,button:1,canvas:1,cite:1,code:1,command:1,datalist:1,del:1,dfn:1,em:1,embed:1,i:1,iframe:1,img:1,input:1,ins:1,kbd:1,keygen:1,label:1,map:1,mark:1,meter:1,noscript:1,object:1,output:1,progress:1,q:1,ruby:1,s:1,samp:1,script:1,select:1,small:1,span:1,strong:1,sub:1,sup:1,textarea:1,time:1,u:1,"var":1,video:1,wbr:1},l,{acronym:1,applet:1,basefont:1,big:1,font:1,isindex:1,strike:1,style:1,tt:1});a(f,e,b,i);c={a:c(b,{a:1,button:1}),abbr:b,address:f,
|
||||
area:j,article:a({style:1},f),aside:a({style:1},f),audio:a({source:1,track:1},f),b:b,base:j,bdi:b,bdo:b,blockquote:f,body:f,br:j,button:c(b,{a:1,button:1}),canvas:b,caption:f,cite:b,code:b,col:j,colgroup:{col:1},command:j,datalist:a({option:1},b),dd:f,del:b,details:a({summary:1},f),dfn:b,div:a({style:1},f),dl:{dt:1,dd:1},dt:f,em:b,embed:j,fieldset:a({legend:1},f),figcaption:f,figure:a({figcaption:1},f),footer:f,form:f,h1:b,h2:b,h3:b,h4:b,h5:b,h6:b,head:a({title:1,base:1},d),header:f,hgroup:{h1:1,
|
||||
h2:1,h3:1,h4:1,h5:1,h6:1},hr:j,html:a({head:1,body:1},f,d),i:b,iframe:l,img:j,input:j,ins:b,kbd:b,keygen:j,label:b,legend:b,li:f,link:j,map:f,mark:b,menu:a({li:1},f),meta:j,meter:c(b,{meter:1}),nav:f,noscript:a({link:1,meta:1,style:1},b),object:a({param:1},b),ol:{li:1},optgroup:{option:1},option:l,output:b,p:b,param:j,pre:b,progress:c(b,{progress:1}),q:b,rp:b,rt:b,ruby:a({rp:1,rt:1},b),s:b,samp:b,script:l,section:a({style:1},f),select:{optgroup:1,option:1},small:b,source:j,span:b,strong:b,style:l,
|
||||
sub:b,summary:b,sup:b,table:{caption:1,colgroup:1,thead:1,tfoot:1,tbody:1,tr:1},tbody:{tr:1},td:f,textarea:l,tfoot:{tr:1},th:f,thead:{tr:1},time:c(b,{time:1}),title:l,tr:{th:1,td:1},track:j,u:b,ul:{li:1},"var":b,video:a({source:1,track:1},f),wbr:j,acronym:b,applet:a({param:1},f),basefont:j,big:b,center:f,dialog:j,dir:{li:1},font:b,isindex:j,noframes:f,strike:b,tt:b};a(c,{$block:a({audio:1,dd:1,dt:1,li:1,video:1},e,i),$blockLimit:{article:1,aside:1,audio:1,body:1,caption:1,details:1,dir:1,div:1,dl:1,
|
||||
fieldset:1,figure:1,footer:1,form:1,header:1,hgroup:1,menu:1,nav:1,ol:1,section:1,table:1,td:1,th:1,tr:1,ul:1,video:1},$cdata:{script:1,style:1},$editable:{address:1,article:1,aside:1,blockquote:1,body:1,details:1,div:1,fieldset:1,footer:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,header:1,hgroup:1,nav:1,p:1,pre:1,section:1},$empty:{area:1,base:1,basefont:1,br:1,col:1,command:1,dialog:1,embed:1,hr:1,img:1,input:1,isindex:1,keygen:1,link:1,meta:1,param:1,source:1,track:1,wbr:1},$inline:b,$list:{dl:1,ol:1,
|
||||
ul:1},$listItem:{dd:1,dt:1,li:1},$nonBodyContent:a({body:1,head:1,html:1},c.head),$nonEditable:{applet:1,audio:1,button:1,embed:1,iframe:1,map:1,object:1,option:1,param:1,script:1,textarea:1,video:1},$object:{applet:1,audio:1,button:1,hr:1,iframe:1,img:1,input:1,object:1,select:1,table:1,textarea:1,video:1},$removeEmpty:{abbr:1,acronym:1,b:1,bdi:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,mark:1,meter:1,output:1,q:1,ruby:1,s:1,samp:1,small:1,span:1,strike:1,strong:1,
|
||||
sub:1,sup:1,time:1,tt:1,u:1,"var":1},$tabIndex:{a:1,area:1,button:1,input:1,object:1,select:1,textarea:1},$tableContent:{caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1},$transparent:{a:1,audio:1,canvas:1,del:1,ins:1,map:1,noscript:1,object:1,video:1},$intermediate:{caption:1,colgroup:1,dd:1,dt:1,figcaption:1,legend:1,li:1,optgroup:1,option:1,rp:1,rt:1,summary:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1}});return c}();CKEDITOR.dom.event=function(a){this.$=a};
|
||||
CKEDITOR.dom.event.prototype={getKey:function(){return this.$.keyCode||this.$.which},getKeystroke:function(){var a=this.getKey();if(this.$.ctrlKey||this.$.metaKey)a=a+CKEDITOR.CTRL;this.$.shiftKey&&(a=a+CKEDITOR.SHIFT);this.$.altKey&&(a=a+CKEDITOR.ALT);return a},preventDefault:function(a){var c=this.$;c.preventDefault?c.preventDefault():c.returnValue=false;a&&this.stopPropagation()},stopPropagation:function(){var a=this.$;a.stopPropagation?a.stopPropagation():a.cancelBubble=true},getTarget:function(){var a=
|
||||
this.$.target||this.$.srcElement;return a?new CKEDITOR.dom.node(a):null},getPhase:function(){return this.$.eventPhase||2},getPageOffset:function(){var a=this.getTarget().getDocument().$;return{x:this.$.pageX||this.$.clientX+(a.documentElement.scrollLeft||a.body.scrollLeft),y:this.$.pageY||this.$.clientY+(a.documentElement.scrollTop||a.body.scrollTop)}}};CKEDITOR.CTRL=1114112;CKEDITOR.SHIFT=2228224;CKEDITOR.ALT=4456448;CKEDITOR.EVENT_PHASE_CAPTURING=1;CKEDITOR.EVENT_PHASE_AT_TARGET=2;
|
||||
CKEDITOR.EVENT_PHASE_BUBBLING=3;CKEDITOR.dom.domObject=function(a){if(a)this.$=a};
|
||||
CKEDITOR.dom.domObject.prototype=function(){var a=function(a,b){return function(f){typeof CKEDITOR!="undefined"&&a.fire(b,new CKEDITOR.dom.event(f))}};return{getPrivate:function(){var a;if(!(a=this.getCustomData("_")))this.setCustomData("_",a={});return a},on:function(c){var b=this.getCustomData("_cke_nativeListeners");if(!b){b={};this.setCustomData("_cke_nativeListeners",b)}if(!b[c]){b=b[c]=a(this,c);this.$.addEventListener?this.$.addEventListener(c,b,!!CKEDITOR.event.useCapture):this.$.attachEvent&&
|
||||
this.$.attachEvent("on"+c,b)}return CKEDITOR.event.prototype.on.apply(this,arguments)},removeListener:function(a){CKEDITOR.event.prototype.removeListener.apply(this,arguments);if(!this.hasListeners(a)){var b=this.getCustomData("_cke_nativeListeners"),f=b&&b[a];if(f){this.$.removeEventListener?this.$.removeEventListener(a,f,false):this.$.detachEvent&&this.$.detachEvent("on"+a,f);delete b[a]}}},removeAllListeners:function(){var a=this.getCustomData("_cke_nativeListeners"),b;for(b in a){var f=a[b];this.$.detachEvent?
|
||||
this.$.detachEvent("on"+b,f):this.$.removeEventListener&&this.$.removeEventListener(b,f,false);delete a[b]}}}}();
|
||||
(function(a){var c={};CKEDITOR.on("reset",function(){c={}});a.equals=function(b){try{return b&&b.$===this.$}catch(a){return false}};a.setCustomData=function(b,a){var e=this.getUniqueId();(c[e]||(c[e]={}))[b]=a;return this};a.getCustomData=function(b){var a=this.$["data-cke-expando"];return(a=a&&c[a])&&b in a?a[b]:null};a.removeCustomData=function(b){var a=this.$["data-cke-expando"],a=a&&c[a],e,d;if(a){e=a[b];d=b in a;delete a[b]}return d?e:null};a.clearCustomData=function(){this.removeAllListeners();
|
||||
var b=this.$["data-cke-expando"];b&&delete c[b]};a.getUniqueId=function(){return this.$["data-cke-expando"]||(this.$["data-cke-expando"]=CKEDITOR.tools.getNextNumber())};CKEDITOR.event.implementOn(a)})(CKEDITOR.dom.domObject.prototype);
|
||||
CKEDITOR.dom.node=function(a){return a?new CKEDITOR.dom[a.nodeType==CKEDITOR.NODE_DOCUMENT?"document":a.nodeType==CKEDITOR.NODE_ELEMENT?"element":a.nodeType==CKEDITOR.NODE_TEXT?"text":a.nodeType==CKEDITOR.NODE_COMMENT?"comment":a.nodeType==CKEDITOR.NODE_DOCUMENT_FRAGMENT?"documentFragment":"domObject"](a):this};CKEDITOR.dom.node.prototype=new CKEDITOR.dom.domObject;CKEDITOR.NODE_ELEMENT=1;CKEDITOR.NODE_DOCUMENT=9;CKEDITOR.NODE_TEXT=3;CKEDITOR.NODE_COMMENT=8;CKEDITOR.NODE_DOCUMENT_FRAGMENT=11;
|
||||
CKEDITOR.POSITION_IDENTICAL=0;CKEDITOR.POSITION_DISCONNECTED=1;CKEDITOR.POSITION_FOLLOWING=2;CKEDITOR.POSITION_PRECEDING=4;CKEDITOR.POSITION_IS_CONTAINED=8;CKEDITOR.POSITION_CONTAINS=16;
|
||||
CKEDITOR.tools.extend(CKEDITOR.dom.node.prototype,{appendTo:function(a,c){a.append(this,c);return a},clone:function(a,c){var b=this.$.cloneNode(a),f=function(b){b["data-cke-expando"]&&(b["data-cke-expando"]=false);if(b.nodeType==CKEDITOR.NODE_ELEMENT){c||b.removeAttribute("id",false);if(a)for(var b=b.childNodes,d=0;d<b.length;d++)f(b[d])}};f(b);return new CKEDITOR.dom.node(b)},hasPrevious:function(){return!!this.$.previousSibling},hasNext:function(){return!!this.$.nextSibling},insertAfter:function(a){a.$.parentNode.insertBefore(this.$,
|
||||
a.$.nextSibling);return a},insertBefore:function(a){a.$.parentNode.insertBefore(this.$,a.$);return a},insertBeforeMe:function(a){this.$.parentNode.insertBefore(a.$,this.$);return a},getAddress:function(a){for(var c=[],b=this.getDocument().$.documentElement,f=this.$;f&&f!=b;){var e=f.parentNode;e&&c.unshift(this.getIndex.call({$:f},a));f=e}return c},getDocument:function(){return new CKEDITOR.dom.document(this.$.ownerDocument||this.$.parentNode.ownerDocument)},getIndex:function(a){var c=this.$,b=-1,
|
||||
f;if(!this.$.parentNode)return b;do if(!a||!(c!=this.$&&c.nodeType==CKEDITOR.NODE_TEXT&&(f||!c.nodeValue))){b++;f=c.nodeType==CKEDITOR.NODE_TEXT}while(c=c.previousSibling);return b},getNextSourceNode:function(a,c,b){if(b&&!b.call)var f=b,b=function(b){return!b.equals(f)};var a=!a&&this.getFirst&&this.getFirst(),e;if(!a){if(this.type==CKEDITOR.NODE_ELEMENT&&b&&b(this,true)===false)return null;a=this.getNext()}for(;!a&&(e=(e||this).getParent());){if(b&&b(e,true)===false)return null;a=e.getNext()}return!a||
|
||||
b&&b(a)===false?null:c&&c!=a.type?a.getNextSourceNode(false,c,b):a},getPreviousSourceNode:function(a,c,b){if(b&&!b.call)var f=b,b=function(b){return!b.equals(f)};var a=!a&&this.getLast&&this.getLast(),e;if(!a){if(this.type==CKEDITOR.NODE_ELEMENT&&b&&b(this,true)===false)return null;a=this.getPrevious()}for(;!a&&(e=(e||this).getParent());){if(b&&b(e,true)===false)return null;a=e.getPrevious()}return!a||b&&b(a)===false?null:c&&a.type!=c?a.getPreviousSourceNode(false,c,b):a},getPrevious:function(a){var c=
|
||||
this.$,b;do b=(c=c.previousSibling)&&c.nodeType!=10&&new CKEDITOR.dom.node(c);while(b&&a&&!a(b));return b},getNext:function(a){var c=this.$,b;do b=(c=c.nextSibling)&&new CKEDITOR.dom.node(c);while(b&&a&&!a(b));return b},getParent:function(a){var c=this.$.parentNode;return c&&(c.nodeType==CKEDITOR.NODE_ELEMENT||a&&c.nodeType==CKEDITOR.NODE_DOCUMENT_FRAGMENT)?new CKEDITOR.dom.node(c):null},getParents:function(a){var c=this,b=[];do b[a?"push":"unshift"](c);while(c=c.getParent());return b},getCommonAncestor:function(a){if(a.equals(this))return this;
|
||||
if(a.contains&&a.contains(this))return a;var c=this.contains?this:this.getParent();do if(c.contains(a))return c;while(c=c.getParent());return null},getPosition:function(a){var c=this.$,b=a.$;if(c.compareDocumentPosition)return c.compareDocumentPosition(b);if(c==b)return CKEDITOR.POSITION_IDENTICAL;if(this.type==CKEDITOR.NODE_ELEMENT&&a.type==CKEDITOR.NODE_ELEMENT){if(c.contains){if(c.contains(b))return CKEDITOR.POSITION_CONTAINS+CKEDITOR.POSITION_PRECEDING;if(b.contains(c))return CKEDITOR.POSITION_IS_CONTAINED+
|
||||
CKEDITOR.POSITION_FOLLOWING}if("sourceIndex"in c)return c.sourceIndex<0||b.sourceIndex<0?CKEDITOR.POSITION_DISCONNECTED:c.sourceIndex<b.sourceIndex?CKEDITOR.POSITION_PRECEDING:CKEDITOR.POSITION_FOLLOWING}for(var c=this.getAddress(),a=a.getAddress(),b=Math.min(c.length,a.length),f=0;f<=b-1;f++)if(c[f]!=a[f]){if(f<b)return c[f]<a[f]?CKEDITOR.POSITION_PRECEDING:CKEDITOR.POSITION_FOLLOWING;break}return c.length<a.length?CKEDITOR.POSITION_CONTAINS+CKEDITOR.POSITION_PRECEDING:CKEDITOR.POSITION_IS_CONTAINED+
|
||||
CKEDITOR.POSITION_FOLLOWING},getAscendant:function(a,c){var b=this.$,f;if(!c)b=b.parentNode;for(;b;){if(b.nodeName&&(f=b.nodeName.toLowerCase(),typeof a=="string"?f==a:f in a))return new CKEDITOR.dom.node(b);try{b=b.parentNode}catch(e){b=null}}return null},hasAscendant:function(a,c){var b=this.$;if(!c)b=b.parentNode;for(;b;){if(b.nodeName&&b.nodeName.toLowerCase()==a)return true;b=b.parentNode}return false},move:function(a,c){a.append(this.remove(),c)},remove:function(a){var c=this.$,b=c.parentNode;
|
||||
if(b){if(a)for(;a=c.firstChild;)b.insertBefore(c.removeChild(a),c);b.removeChild(c)}return this},replace:function(a){this.insertBefore(a);a.remove()},trim:function(){this.ltrim();this.rtrim()},ltrim:function(){for(var a;this.getFirst&&(a=this.getFirst());){if(a.type==CKEDITOR.NODE_TEXT){var c=CKEDITOR.tools.ltrim(a.getText()),b=a.getLength();if(c){if(c.length<b){a.split(b-c.length);this.$.removeChild(this.$.firstChild)}}else{a.remove();continue}}break}},rtrim:function(){for(var a;this.getLast&&(a=
|
||||
this.getLast());){if(a.type==CKEDITOR.NODE_TEXT){var c=CKEDITOR.tools.rtrim(a.getText()),b=a.getLength();if(c){if(c.length<b){a.split(c.length);this.$.lastChild.parentNode.removeChild(this.$.lastChild)}}else{a.remove();continue}}break}if(!CKEDITOR.env.ie&&!CKEDITOR.env.opera)(a=this.$.lastChild)&&(a.type==1&&a.nodeName.toLowerCase()=="br")&&a.parentNode.removeChild(a)},isReadOnly:function(){var a=this;this.type!=CKEDITOR.NODE_ELEMENT&&(a=this.getParent());if(a&&typeof a.$.isContentEditable!="undefined")return!(a.$.isContentEditable||
|
||||
a.data("cke-editable"));for(;a;){if(a.data("cke-editable"))break;if(a.getAttribute("contentEditable")=="false")return true;if(a.getAttribute("contentEditable")=="true")break;a=a.getParent()}return!a}});CKEDITOR.dom.window=function(a){CKEDITOR.dom.domObject.call(this,a)};CKEDITOR.dom.window.prototype=new CKEDITOR.dom.domObject;
|
||||
CKEDITOR.tools.extend(CKEDITOR.dom.window.prototype,{focus:function(){this.$.focus()},getViewPaneSize:function(){var a=this.$.document,c=a.compatMode=="CSS1Compat";return{width:(c?a.documentElement.clientWidth:a.body.clientWidth)||0,height:(c?a.documentElement.clientHeight:a.body.clientHeight)||0}},getScrollPosition:function(){var a=this.$;if("pageXOffset"in a)return{x:a.pageXOffset||0,y:a.pageYOffset||0};a=a.document;return{x:a.documentElement.scrollLeft||a.body.scrollLeft||0,y:a.documentElement.scrollTop||
|
||||
a.body.scrollTop||0}},getFrame:function(){var a=this.$.frameElement;return a?new CKEDITOR.dom.element.get(a):null}});CKEDITOR.dom.document=function(a){CKEDITOR.dom.domObject.call(this,a)};CKEDITOR.dom.document.prototype=new CKEDITOR.dom.domObject;
|
||||
CKEDITOR.tools.extend(CKEDITOR.dom.document.prototype,{type:CKEDITOR.NODE_DOCUMENT,appendStyleSheet:function(a){if(this.$.createStyleSheet)this.$.createStyleSheet(a);else{var c=new CKEDITOR.dom.element("link");c.setAttributes({rel:"stylesheet",type:"text/css",href:a});this.getHead().append(c)}},appendStyleText:function(a){if(this.$.createStyleSheet){var c=this.$.createStyleSheet("");c.cssText=a}else{var b=new CKEDITOR.dom.element("style",this);b.append(new CKEDITOR.dom.text(a,this));this.getHead().append(b)}return c||
|
||||
b.$.sheet},createElement:function(a,c){var b=new CKEDITOR.dom.element(a,this);if(c){c.attributes&&b.setAttributes(c.attributes);c.styles&&b.setStyles(c.styles)}return b},createText:function(a){return new CKEDITOR.dom.text(a,this)},focus:function(){this.getWindow().focus()},getActive:function(){return new CKEDITOR.dom.element(this.$.activeElement)},getById:function(a){return(a=this.$.getElementById(a))?new CKEDITOR.dom.element(a):null},getByAddress:function(a,c){for(var b=this.$.documentElement,f=
|
||||
0;b&&f<a.length;f++){var e=a[f];if(c)for(var d=-1,j=0;j<b.childNodes.length;j++){var l=b.childNodes[j];if(!(c===true&&l.nodeType==3&&l.previousSibling&&l.previousSibling.nodeType==3)){d++;if(d==e){b=l;break}}}else b=b.childNodes[e]}return b?new CKEDITOR.dom.node(b):null},getElementsByTag:function(a,c){if((!CKEDITOR.env.ie||document.documentMode>8)&&c)a=c+":"+a;return new CKEDITOR.dom.nodeList(this.$.getElementsByTagName(a))},getHead:function(){var a=this.$.getElementsByTagName("head")[0];return a=
|
||||
a?new CKEDITOR.dom.element(a):this.getDocumentElement().append(new CKEDITOR.dom.element("head"),true)},getBody:function(){return new CKEDITOR.dom.element(this.$.body)},getDocumentElement:function(){return new CKEDITOR.dom.element(this.$.documentElement)},getWindow:function(){var a=new CKEDITOR.dom.window(this.$.parentWindow||this.$.defaultView);return(this.getWindow=function(){return a})()},write:function(a){this.$.open("text/html","replace");CKEDITOR.env.isCustomDomain()&&(this.$.domain=document.domain);
|
||||
this.$.write(a);this.$.close()}});CKEDITOR.dom.nodeList=function(a){this.$=a};CKEDITOR.dom.nodeList.prototype={count:function(){return this.$.length},getItem:function(a){if(a<0||a>=this.$.length)return null;return(a=this.$[a])?new CKEDITOR.dom.node(a):null}};CKEDITOR.dom.element=function(a,c){typeof a=="string"&&(a=(c?c.$:document).createElement(a));CKEDITOR.dom.domObject.call(this,a)};
|
||||
CKEDITOR.dom.element.get=function(a){return(a=typeof a=="string"?document.getElementById(a)||document.getElementsByName(a)[0]:a)&&(a.$?a:new CKEDITOR.dom.element(a))};CKEDITOR.dom.element.prototype=new CKEDITOR.dom.node;CKEDITOR.dom.element.createFromHtml=function(a,c){var b=new CKEDITOR.dom.element("div",c);b.setHtml(a);return b.getFirst().remove()};
|
||||
CKEDITOR.dom.element.setMarker=function(a,c,b,f){var e=c.getCustomData("list_marker_id")||c.setCustomData("list_marker_id",CKEDITOR.tools.getNextNumber()).getCustomData("list_marker_id"),d=c.getCustomData("list_marker_names")||c.setCustomData("list_marker_names",{}).getCustomData("list_marker_names");a[e]=c;d[b]=1;return c.setCustomData(b,f)};CKEDITOR.dom.element.clearAllMarkers=function(a){for(var c in a)CKEDITOR.dom.element.clearMarkers(a,a[c],1)};
|
||||
CKEDITOR.dom.element.clearMarkers=function(a,c,b){var f=c.getCustomData("list_marker_names"),e=c.getCustomData("list_marker_id"),d;for(d in f)c.removeCustomData(d);c.removeCustomData("list_marker_names");if(b){c.removeCustomData("list_marker_id");delete a[e]}};
|
||||
(function(){function a(b){for(var a=0,e=0,d=c[b].length;e<d;e++)a=a+(parseInt(this.getComputedStyle(c[b][e])||0,10)||0);return a}CKEDITOR.tools.extend(CKEDITOR.dom.element.prototype,{type:CKEDITOR.NODE_ELEMENT,addClass:function(b){var a=this.$.className;a&&(RegExp("(?:^|\\s)"+b+"(?:\\s|$)","").test(a)||(a=a+(" "+b)));this.$.className=a||b},removeClass:function(b){var a=this.getAttribute("class");if(a){b=RegExp("(?:^|\\s+)"+b+"(?=\\s|$)","i");if(b.test(a))(a=a.replace(b,"").replace(/^\s+/,""))?this.setAttribute("class",
|
||||
a):this.removeAttribute("class")}return this},hasClass:function(b){return RegExp("(?:^|\\s+)"+b+"(?=\\s|$)","").test(this.getAttribute("class"))},append:function(b,a){typeof b=="string"&&(b=this.getDocument().createElement(b));a?this.$.insertBefore(b.$,this.$.firstChild):this.$.appendChild(b.$);return b},appendHtml:function(b){if(this.$.childNodes.length){var a=new CKEDITOR.dom.element("div",this.getDocument());a.setHtml(b);a.moveChildren(this)}else this.setHtml(b)},appendText:function(b){this.$.text!=
|
||||
void 0?this.$.text=this.$.text+b:this.append(new CKEDITOR.dom.text(b))},appendBogus:function(){for(var b=this.getLast();b&&b.type==CKEDITOR.NODE_TEXT&&!CKEDITOR.tools.rtrim(b.getText());)b=b.getPrevious();if(!b||!b.is||!b.is("br")){b=CKEDITOR.env.opera?this.getDocument().createText(""):this.getDocument().createElement("br");CKEDITOR.env.gecko&&b.setAttribute("type","_moz");this.append(b)}},breakParent:function(b){var a=new CKEDITOR.dom.range(this.getDocument());a.setStartAfter(this);a.setEndAfter(b);
|
||||
b=a.extractContents();a.insertNode(this.remove());b.insertAfterNode(this)},contains:CKEDITOR.env.ie||CKEDITOR.env.webkit?function(b){var a=this.$;return b.type!=CKEDITOR.NODE_ELEMENT?a.contains(b.getParent().$):a!=b.$&&a.contains(b.$)}:function(b){return!!(this.$.compareDocumentPosition(b.$)&16)},focus:function(){function b(){try{this.$.focus()}catch(b){}}return function(a){a?CKEDITOR.tools.setTimeout(b,100,this):b.call(this)}}(),getHtml:function(){var b=this.$.innerHTML;return CKEDITOR.env.ie?b.replace(/<\?[^>]*>/g,
|
||||
""):b},getOuterHtml:function(){if(this.$.outerHTML)return this.$.outerHTML.replace(/<\?[^>]*>/,"");var b=this.$.ownerDocument.createElement("div");b.appendChild(this.$.cloneNode(true));return b.innerHTML},getClientRect:function(){var b=CKEDITOR.tools.extend({},this.$.getBoundingClientRect());!b.width&&(b.width=b.right-b.left);!b.height&&(b.height=b.bottom-b.top);return b},setHtml:function(){var b=function(b){return this.$.innerHTML=b};return CKEDITOR.env.ie&&CKEDITOR.env.version<9?function(b){try{return this.$.innerHTML=
|
||||
b}catch(a){this.$.innerHTML="";var c=new CKEDITOR.dom.element("body",this.getDocument());c.$.innerHTML=b;for(c=c.getChildren();c.count();)this.append(c.getItem(0));return b}}:b}(),setText:function(b){CKEDITOR.dom.element.prototype.setText=this.$.innerText!=void 0?function(b){return this.$.innerText=b}:function(b){return this.$.textContent=b};return this.setText(b)},getAttribute:function(){var b=function(b){return this.$.getAttribute(b,2)};return CKEDITOR.env.ie&&(CKEDITOR.env.ie7Compat||CKEDITOR.env.ie6Compat)?
|
||||
function(b){switch(b){case "class":b="className";break;case "http-equiv":b="httpEquiv";break;case "name":return this.$.name;case "tabindex":b=this.$.getAttribute(b,2);b!==0&&this.$.tabIndex===0&&(b=null);return b;case "checked":b=this.$.attributes.getNamedItem(b);return(b.specified?b.nodeValue:this.$.checked)?"checked":null;case "hspace":case "value":return this.$[b];case "style":return this.$.style.cssText;case "contenteditable":case "contentEditable":return this.$.attributes.getNamedItem("contentEditable").specified?
|
||||
this.$.getAttribute("contentEditable"):null}return this.$.getAttribute(b,2)}:b}(),getChildren:function(){return new CKEDITOR.dom.nodeList(this.$.childNodes)},getComputedStyle:CKEDITOR.env.ie?function(b){return this.$.currentStyle[CKEDITOR.tools.cssStyleToDomStyle(b)]}:function(b){var a=this.getWindow().$.getComputedStyle(this.$,null);return a?a.getPropertyValue(b):""},getDtd:function(){var b=CKEDITOR.dtd[this.getName()];this.getDtd=function(){return b};return b},getElementsByTag:CKEDITOR.dom.document.prototype.getElementsByTag,
|
||||
getTabIndex:CKEDITOR.env.ie?function(){var b=this.$.tabIndex;b===0&&(!CKEDITOR.dtd.$tabIndex[this.getName()]&&parseInt(this.getAttribute("tabindex"),10)!==0)&&(b=-1);return b}:CKEDITOR.env.webkit?function(){var b=this.$.tabIndex;if(b==void 0){b=parseInt(this.getAttribute("tabindex"),10);isNaN(b)&&(b=-1)}return b}:function(){return this.$.tabIndex},getText:function(){return this.$.textContent||this.$.innerText||""},getWindow:function(){return this.getDocument().getWindow()},getId:function(){return this.$.id||
|
||||
null},getNameAtt:function(){return this.$.name||null},getName:function(){var b=this.$.nodeName.toLowerCase();if(CKEDITOR.env.ie&&!(document.documentMode>8)){var a=this.$.scopeName;a!="HTML"&&(b=a.toLowerCase()+":"+b)}return(this.getName=function(){return b})()},getValue:function(){return this.$.value},getFirst:function(b){var a=this.$.firstChild;(a=a&&new CKEDITOR.dom.node(a))&&(b&&!b(a))&&(a=a.getNext(b));return a},getLast:function(b){var a=this.$.lastChild;(a=a&&new CKEDITOR.dom.node(a))&&(b&&!b(a))&&
|
||||
(a=a.getPrevious(b));return a},getStyle:function(b){return this.$.style[CKEDITOR.tools.cssStyleToDomStyle(b)]},is:function(){var b=this.getName();if(typeof arguments[0]=="object")return!!arguments[0][b];for(var a=0;a<arguments.length;a++)if(arguments[a]==b)return true;return false},isEditable:function(b){var a=this.getName();if(this.isReadOnly()||this.getComputedStyle("display")=="none"||this.getComputedStyle("visibility")=="hidden"||CKEDITOR.dtd.$nonEditable[a]||CKEDITOR.dtd.$empty[a]||this.is("a")&&
|
||||
(this.data("cke-saved-name")||this.hasAttribute("name"))&&!this.getChildCount())return false;if(b!==false){b=CKEDITOR.dtd[a]||CKEDITOR.dtd.span;return!(!b||!b["#"])}return true},isIdentical:function(b){var a=this.clone(0,1),b=b.clone(0,1);a.removeAttributes(["_moz_dirty","data-cke-expando","data-cke-saved-href","data-cke-saved-name"]);b.removeAttributes(["_moz_dirty","data-cke-expando","data-cke-saved-href","data-cke-saved-name"]);if(a.$.isEqualNode){a.$.style.cssText=CKEDITOR.tools.normalizeCssText(a.$.style.cssText);
|
||||
b.$.style.cssText=CKEDITOR.tools.normalizeCssText(b.$.style.cssText);return a.$.isEqualNode(b.$)}a=a.getOuterHtml();b=b.getOuterHtml();if(CKEDITOR.env.ie&&CKEDITOR.env.version<9&&this.is("a")){var c=this.getParent();if(c.type==CKEDITOR.NODE_ELEMENT){c=c.clone();c.setHtml(a);a=c.getHtml();c.setHtml(b);b=c.getHtml()}}return a==b},isVisible:function(){var b=(this.$.offsetHeight||this.$.offsetWidth)&&this.getComputedStyle("visibility")!="hidden",a,c;if(b&&(CKEDITOR.env.webkit||CKEDITOR.env.opera)){a=
|
||||
this.getWindow();if(!a.equals(CKEDITOR.document.getWindow())&&(c=a.$.frameElement))b=(new CKEDITOR.dom.element(c)).isVisible()}return!!b},isEmptyInlineRemoveable:function(){if(!CKEDITOR.dtd.$removeEmpty[this.getName()])return false;for(var b=this.getChildren(),a=0,c=b.count();a<c;a++){var d=b.getItem(a);if(!(d.type==CKEDITOR.NODE_ELEMENT&&d.data("cke-bookmark"))&&(d.type==CKEDITOR.NODE_ELEMENT&&!d.isEmptyInlineRemoveable()||d.type==CKEDITOR.NODE_TEXT&&CKEDITOR.tools.trim(d.getText())))return false}return true},
|
||||
hasAttributes:CKEDITOR.env.ie&&(CKEDITOR.env.ie7Compat||CKEDITOR.env.ie6Compat)?function(){for(var b=this.$.attributes,a=0;a<b.length;a++){var c=b[a];switch(c.nodeName){case "class":if(this.getAttribute("class"))return true;case "data-cke-expando":continue;default:if(c.specified)return true}}return false}:function(){var b=this.$.attributes,a=b.length,c={"data-cke-expando":1,_moz_dirty:1};return a>0&&(a>2||!c[b[0].nodeName]||a==2&&!c[b[1].nodeName])},hasAttribute:function(){function b(b){b=this.$.attributes.getNamedItem(b);
|
||||
return!(!b||!b.specified)}return CKEDITOR.env.ie&&CKEDITOR.env.version<8?function(a){return a=="name"?!!this.$.name:b.call(this,a)}:b}(),hide:function(){this.setStyle("display","none")},moveChildren:function(b,a){var c=this.$,b=b.$;if(c!=b){var d;if(a)for(;d=c.lastChild;)b.insertBefore(c.removeChild(d),b.firstChild);else for(;d=c.firstChild;)b.appendChild(c.removeChild(d))}},mergeSiblings:function(){function b(b,a,c){if(a&&a.type==CKEDITOR.NODE_ELEMENT){for(var j=[];a.data("cke-bookmark")||a.isEmptyInlineRemoveable();){j.push(a);
|
||||
a=c?a.getNext():a.getPrevious();if(!a||a.type!=CKEDITOR.NODE_ELEMENT)return}if(b.isIdentical(a)){for(var l=c?b.getLast():b.getFirst();j.length;)j.shift().move(b,!c);a.moveChildren(b,!c);a.remove();l&&l.type==CKEDITOR.NODE_ELEMENT&&l.mergeSiblings()}}}return function(a){if(a===false||CKEDITOR.dtd.$removeEmpty[this.getName()]||this.is("a")){b(this,this.getNext(),true);b(this,this.getPrevious())}}}(),show:function(){this.setStyles({display:"",visibility:""})},setAttribute:function(){var b=function(b,
|
||||
a){this.$.setAttribute(b,a);return this};return CKEDITOR.env.ie&&(CKEDITOR.env.ie7Compat||CKEDITOR.env.ie6Compat)?function(a,c){a=="class"?this.$.className=c:a=="style"?this.$.style.cssText=c:a=="tabindex"?this.$.tabIndex=c:a=="checked"?this.$.checked=c:a=="contenteditable"?b.call(this,"contentEditable",c):b.apply(this,arguments);return this}:CKEDITOR.env.ie8Compat&&CKEDITOR.env.secure?function(a,c){if(a=="src"&&c.match(/^http:\/\//))try{b.apply(this,arguments)}catch(d){}else b.apply(this,arguments);
|
||||
return this}:b}(),setAttributes:function(b){for(var a in b)this.setAttribute(a,b[a]);return this},setValue:function(b){this.$.value=b;return this},removeAttribute:function(){var b=function(b){this.$.removeAttribute(b)};return CKEDITOR.env.ie&&(CKEDITOR.env.ie7Compat||CKEDITOR.env.ie6Compat)?function(b){b=="class"?b="className":b=="tabindex"?b="tabIndex":b=="contenteditable"&&(b="contentEditable");this.$.removeAttribute(b)}:b}(),removeAttributes:function(b){if(CKEDITOR.tools.isArray(b))for(var a=0;a<
|
||||
b.length;a++)this.removeAttribute(b[a]);else for(a in b)b.hasOwnProperty(a)&&this.removeAttribute(a)},removeStyle:function(b){var a=this.$.style;if(!a.removeProperty&&(b=="border"||b=="margin"||b=="padding")){var c=["top","left","right","bottom"],d;b=="border"&&(d=["color","style","width"]);for(var a=[],j=0;j<c.length;j++)if(d)for(var l=0;l<d.length;l++)a.push([b,c[j],d[l]].join("-"));else a.push([b,c[j]].join("-"));for(b=0;b<a.length;b++)this.removeStyle(a[b])}else{a.removeProperty?a.removeProperty(b):
|
||||
a.removeAttribute(CKEDITOR.tools.cssStyleToDomStyle(b));this.$.style.cssText||this.removeAttribute("style")}},setStyle:function(b,a){this.$.style[CKEDITOR.tools.cssStyleToDomStyle(b)]=a;return this},setStyles:function(b){for(var a in b)this.setStyle(a,b[a]);return this},setOpacity:function(b){if(CKEDITOR.env.ie&&CKEDITOR.env.version<9){b=Math.round(b*100);this.setStyle("filter",b>=100?"":"progid:DXImageTransform.Microsoft.Alpha(opacity="+b+")")}else this.setStyle("opacity",b)},unselectable:function(){this.setStyles(CKEDITOR.tools.cssVendorPrefix("user-select",
|
||||
"none"));if(CKEDITOR.env.ie||CKEDITOR.env.opera){this.setAttribute("unselectable","on");for(var b,a=this.getElementsByTag("*"),c=0,d=a.count();c<d;c++){b=a.getItem(c);b.setAttribute("unselectable","on")}}},getPositionedAncestor:function(){for(var b=this;b.getName()!="html";){if(b.getComputedStyle("position")!="static")return b;b=b.getParent()}return null},getDocumentPosition:function(b){var a=0,c=0,d=this.getDocument(),j=d.getBody(),l=d.$.compatMode=="BackCompat";if(document.documentElement.getBoundingClientRect){var i=
|
||||
this.$.getBoundingClientRect(),m=d.$.documentElement,n=m.clientTop||j.$.clientTop||0,r=m.clientLeft||j.$.clientLeft||0,p=true;if(CKEDITOR.env.ie){p=d.getDocumentElement().contains(this);d=d.getBody().contains(this);p=l&&d||!l&&p}if(p){a=i.left+(!l&&m.scrollLeft||j.$.scrollLeft);a=a-r;c=i.top+(!l&&m.scrollTop||j.$.scrollTop);c=c-n}}else{j=this;for(d=null;j&&!(j.getName()=="body"||j.getName()=="html");){a=a+(j.$.offsetLeft-j.$.scrollLeft);c=c+(j.$.offsetTop-j.$.scrollTop);if(!j.equals(this)){a=a+(j.$.clientLeft||
|
||||
0);c=c+(j.$.clientTop||0)}for(;d&&!d.equals(j);){a=a-d.$.scrollLeft;c=c-d.$.scrollTop;d=d.getParent()}d=j;j=(i=j.$.offsetParent)?new CKEDITOR.dom.element(i):null}}if(b){j=this.getWindow();d=b.getWindow();if(!j.equals(d)&&j.$.frameElement){b=(new CKEDITOR.dom.element(j.$.frameElement)).getDocumentPosition(b);a=a+b.x;c=c+b.y}}if(!document.documentElement.getBoundingClientRect&&CKEDITOR.env.gecko&&!l){a=a+(this.$.clientLeft?1:0);c=c+(this.$.clientTop?1:0)}return{x:a,y:c}},scrollIntoView:function(b){var a=
|
||||
this.getParent();if(a){do{(a.$.clientWidth&&a.$.clientWidth<a.$.scrollWidth||a.$.clientHeight&&a.$.clientHeight<a.$.scrollHeight)&&!a.is("body")&&this.scrollIntoParent(a,b,1);if(a.is("html")){var c=a.getWindow();try{var d=c.$.frameElement;d&&(a=new CKEDITOR.dom.element(d))}catch(j){}}}while(a=a.getParent())}},scrollIntoParent:function(b,a,c){var d,j,l,i;function m(a,c){if(/body|html/.test(b.getName()))b.getWindow().$.scrollBy(a,c);else{b.$.scrollLeft=b.$.scrollLeft+a;b.$.scrollTop=b.$.scrollTop+c}}
|
||||
function n(b,a){var c={x:0,y:0};if(!b.is(p?"body":"html")){var g=b.$.getBoundingClientRect();c.x=g.left;c.y=g.top}g=b.getWindow();if(!g.equals(a)){g=n(CKEDITOR.dom.element.get(g.$.frameElement),a);c.x=c.x+g.x;c.y=c.y+g.y}return c}function r(b,a){return parseInt(b.getComputedStyle("margin-"+a)||0,10)||0}!b&&(b=this.getWindow());l=b.getDocument();var p=l.$.compatMode=="BackCompat";b instanceof CKEDITOR.dom.window&&(b=p?l.getBody():l.getDocumentElement());l=b.getWindow();j=n(this,l);var g=n(b,l),h=this.$.offsetHeight;
|
||||
d=this.$.offsetWidth;var u=b.$.clientHeight,w=b.$.clientWidth;l=j.x-r(this,"left")-g.x||0;i=j.y-r(this,"top")-g.y||0;d=j.x+d+r(this,"right")-(g.x+w)||0;j=j.y+h+r(this,"bottom")-(g.y+u)||0;if(i<0||j>0)m(0,a===true?i:a===false?j:i<0?i:j);if(c&&(l<0||d>0))m(l<0?l:d,0)},setState:function(b,a,c){a=a||"cke";switch(b){case CKEDITOR.TRISTATE_ON:this.addClass(a+"_on");this.removeClass(a+"_off");this.removeClass(a+"_disabled");c&&this.setAttribute("aria-pressed",true);c&&this.removeAttribute("aria-disabled");
|
||||
break;case CKEDITOR.TRISTATE_DISABLED:this.addClass(a+"_disabled");this.removeClass(a+"_off");this.removeClass(a+"_on");c&&this.setAttribute("aria-disabled",true);c&&this.removeAttribute("aria-pressed");break;default:this.addClass(a+"_off");this.removeClass(a+"_on");this.removeClass(a+"_disabled");c&&this.removeAttribute("aria-pressed");c&&this.removeAttribute("aria-disabled")}},getFrameDocument:function(){var b=this.$;try{b.contentWindow.document}catch(a){b.src=b.src}return b&&new CKEDITOR.dom.document(b.contentWindow.document)},
|
||||
copyAttributes:function(b,a){for(var c=this.$.attributes,a=a||{},d=0;d<c.length;d++){var j=c[d],l=j.nodeName.toLowerCase(),i;if(!(l in a))if(l=="checked"&&(i=this.getAttribute(l)))b.setAttribute(l,i);else if(j.specified||CKEDITOR.env.ie&&j.nodeValue&&l=="value"){i=this.getAttribute(l);if(i===null)i=j.nodeValue;b.setAttribute(l,i)}}if(this.$.style.cssText!=="")b.$.style.cssText=this.$.style.cssText},renameNode:function(b){if(this.getName()!=b){var a=this.getDocument(),b=new CKEDITOR.dom.element(b,
|
||||
a);this.copyAttributes(b);this.moveChildren(b);this.getParent()&&this.$.parentNode.replaceChild(b.$,this.$);b.$["data-cke-expando"]=this.$["data-cke-expando"];this.$=b.$}},getChild:function(){function b(b,a){var c=b.childNodes;if(a>=0&&a<c.length)return c[a]}return function(a){var c=this.$;if(a.slice)for(;a.length>0&&c;)c=b(c,a.shift());else c=b(c,a);return c?new CKEDITOR.dom.node(c):null}}(),getChildCount:function(){return this.$.childNodes.length},disableContextMenu:function(){this.on("contextmenu",
|
||||
function(b){b.data.getTarget().hasClass("cke_enable_context_menu")||b.data.preventDefault()})},getDirection:function(b){return b?this.getComputedStyle("direction")||this.getDirection()||this.getParent()&&this.getParent().getDirection(1)||this.getDocument().$.dir||"ltr":this.getStyle("direction")||this.getAttribute("dir")},data:function(b,a){b="data-"+b;if(a===void 0)return this.getAttribute(b);a===false?this.removeAttribute(b):this.setAttribute(b,a);return null},getEditor:function(){var b=CKEDITOR.instances,
|
||||
a,c;for(a in b){c=b[a];if(c.element.equals(this)&&c.elementMode!=CKEDITOR.ELEMENT_MODE_APPENDTO)return c}return null}});var c={width:["border-left-width","border-right-width","padding-left","padding-right"],height:["border-top-width","border-bottom-width","padding-top","padding-bottom"]};CKEDITOR.dom.element.prototype.setSize=function(b,c,e){if(typeof c=="number"){if(e&&(!CKEDITOR.env.ie||!CKEDITOR.env.quirks))c=c-a.call(this,b);this.setStyle(b,c+"px")}};CKEDITOR.dom.element.prototype.getSize=function(b,
|
||||
c){var e=Math.max(this.$["offset"+CKEDITOR.tools.capitalize(b)],this.$["client"+CKEDITOR.tools.capitalize(b)])||0;c&&(e=e-a.call(this,b));return e}})();CKEDITOR.dom.documentFragment=function(a){a=a||CKEDITOR.document;this.$=a.type==CKEDITOR.NODE_DOCUMENT?a.$.createDocumentFragment():a};
|
||||
CKEDITOR.tools.extend(CKEDITOR.dom.documentFragment.prototype,CKEDITOR.dom.element.prototype,{type:CKEDITOR.NODE_DOCUMENT_FRAGMENT,insertAfterNode:function(a){a=a.$;a.parentNode.insertBefore(this.$,a.nextSibling)}},!0,{append:1,appendBogus:1,getFirst:1,getLast:1,getParent:1,getNext:1,getPrevious:1,appendTo:1,moveChildren:1,insertBefore:1,insertAfterNode:1,replace:1,trim:1,type:1,ltrim:1,rtrim:1,getDocument:1,getChildCount:1,getChild:1,getChildren:1});
|
||||
(function(){function a(a,b){var c=this.range;if(this._.end)return null;if(!this._.start){this._.start=1;if(c.collapsed){this.end();return null}c.optimize()}var d,n=c.startContainer;d=c.endContainer;var r=c.startOffset,p=c.endOffset,g,h=this.guard,u=this.type,f=a?"getPreviousSourceNode":"getNextSourceNode";if(!a&&!this._.guardLTR){var k=d.type==CKEDITOR.NODE_ELEMENT?d:d.getParent(),e=d.type==CKEDITOR.NODE_ELEMENT?d.getChild(p):d.getNext();this._.guardLTR=function(a,b){return(!b||!k.equals(a))&&(!e||
|
||||
!a.equals(e))&&(a.type!=CKEDITOR.NODE_ELEMENT||!b||!a.equals(c.root))}}if(a&&!this._.guardRTL){var F=n.type==CKEDITOR.NODE_ELEMENT?n:n.getParent(),D=n.type==CKEDITOR.NODE_ELEMENT?r?n.getChild(r-1):null:n.getPrevious();this._.guardRTL=function(a,b){return(!b||!F.equals(a))&&(!D||!a.equals(D))&&(a.type!=CKEDITOR.NODE_ELEMENT||!b||!a.equals(c.root))}}var B=a?this._.guardRTL:this._.guardLTR;g=h?function(a,b){return B(a,b)===false?false:h(a,b)}:B;if(this.current)d=this.current[f](false,u,g);else{if(a)d.type==
|
||||
CKEDITOR.NODE_ELEMENT&&(d=p>0?d.getChild(p-1):g(d,true)===false?null:d.getPreviousSourceNode(true,u,g));else{d=n;if(d.type==CKEDITOR.NODE_ELEMENT&&!(d=d.getChild(r)))d=g(n,true)===false?null:n.getNextSourceNode(true,u,g)}d&&g(d)===false&&(d=null)}for(;d&&!this._.end;){this.current=d;if(!this.evaluator||this.evaluator(d)!==false){if(!b)return d}else if(b&&this.evaluator)return false;d=d[f](false,u,g)}this.end();return this.current=null}function c(b){for(var c,d=null;c=a.call(this,b);)d=c;return d}
|
||||
CKEDITOR.dom.walker=CKEDITOR.tools.createClass({$:function(a){this.range=a;this._={}},proto:{end:function(){this._.end=1},next:function(){return a.call(this)},previous:function(){return a.call(this,1)},checkForward:function(){return a.call(this,0,1)!==false},checkBackward:function(){return a.call(this,1,1)!==false},lastForward:function(){return c.call(this)},lastBackward:function(){return c.call(this,1)},reset:function(){delete this.current;this._={}}}});var b={block:1,"list-item":1,table:1,"table-row-group":1,
|
||||
"table-header-group":1,"table-footer-group":1,"table-row":1,"table-column-group":1,"table-column":1,"table-cell":1,"table-caption":1};CKEDITOR.dom.element.prototype.isBlockBoundary=function(a){a=a?CKEDITOR.tools.extend({},CKEDITOR.dtd.$block,a||{}):CKEDITOR.dtd.$block;return this.getComputedStyle("float")=="none"&&b[this.getComputedStyle("display")]||a[this.getName()]};CKEDITOR.dom.walker.blockBoundary=function(a){return function(b){return!(b.type==CKEDITOR.NODE_ELEMENT&&b.isBlockBoundary(a))}};CKEDITOR.dom.walker.listItemBoundary=
|
||||
function(){return this.blockBoundary({br:1})};CKEDITOR.dom.walker.bookmark=function(a,b){function c(a){return a&&a.getName&&a.getName()=="span"&&a.data("cke-bookmark")}return function(d){var n,r;n=d&&d.type!=CKEDITOR.NODE_ELEMENT&&(r=d.getParent())&&c(r);n=a?n:n||c(d);return!!(b^n)}};CKEDITOR.dom.walker.whitespaces=function(a){return function(b){var c;b&&b.type==CKEDITOR.NODE_TEXT&&(c=!CKEDITOR.tools.trim(b.getText())||CKEDITOR.env.webkit&&b.getText()=="");return!!(a^c)}};CKEDITOR.dom.walker.invisible=
|
||||
function(a){var b=CKEDITOR.dom.walker.whitespaces();return function(c){if(b(c))c=1;else{c.type==CKEDITOR.NODE_TEXT&&(c=c.getParent());c=!c.$.offsetHeight}return!!(a^c)}};CKEDITOR.dom.walker.nodeType=function(a,b){return function(c){return!!(b^c.type==a)}};CKEDITOR.dom.walker.bogus=function(a){function b(a){return!e(a)&&!d(a)}return function(c){var d=!CKEDITOR.env.ie?c.is&&c.is("br"):c.getText&&f.test(c.getText());if(d){d=c.getParent();c=c.getNext(b);d=d.isBlockBoundary()&&(!c||c.type==CKEDITOR.NODE_ELEMENT&&
|
||||
c.isBlockBoundary())}return!!(a^d)}};var f=/^[\t\r\n ]*(?: |\xa0)$/,e=CKEDITOR.dom.walker.whitespaces(),d=CKEDITOR.dom.walker.bookmark();CKEDITOR.dom.element.prototype.getBogus=function(){var a=this;do a=a.getPreviousSourceNode();while(d(a)||e(a)||a.type==CKEDITOR.NODE_ELEMENT&&a.getName()in CKEDITOR.dtd.$inline&&!(a.getName()in CKEDITOR.dtd.$empty));return a&&(!CKEDITOR.env.ie?a.is&&a.is("br"):a.getText&&f.test(a.getText()))?a:false}})();
|
||||
CKEDITOR.dom.range=function(a){this.endOffset=this.endContainer=this.startOffset=this.startContainer=null;this.collapsed=true;var c=a instanceof CKEDITOR.dom.document;this.document=c?a:a.getDocument();this.root=c?a.getBody():a};
|
||||
(function(){function a(){var a=false,b=CKEDITOR.dom.walker.whitespaces(),c=CKEDITOR.dom.walker.bookmark(true),g=CKEDITOR.dom.walker.bogus();return function(h){if(c(h)||b(h))return true;if(g(h)&&!a)return a=true;return h.type==CKEDITOR.NODE_TEXT&&(h.hasAscendant("pre")||CKEDITOR.tools.trim(h.getText()).length)||h.type==CKEDITOR.NODE_ELEMENT&&!h.is(d)?false:true}}function c(a){var b=CKEDITOR.dom.walker.whitespaces(),c=CKEDITOR.dom.walker.bookmark(1);return function(g){return c(g)||b(g)?true:!a&&j(g)||
|
||||
g.type==CKEDITOR.NODE_ELEMENT&&g.is(CKEDITOR.dtd.$removeEmpty)}}function b(a){return!l(a)&&!i(a)}var f=function(a){a.collapsed=a.startContainer&&a.endContainer&&a.startContainer.equals(a.endContainer)&&a.startOffset==a.endOffset},e=function(a,b,c,g){a.optimizeBookmark();var h=a.startContainer,d=a.endContainer,f=a.startOffset,k=a.endOffset,e,j;if(d.type==CKEDITOR.NODE_TEXT)d=d.split(k);else if(d.getChildCount()>0)if(k>=d.getChildCount()){d=d.append(a.document.createText(""));j=true}else d=d.getChild(k);
|
||||
if(h.type==CKEDITOR.NODE_TEXT){h.split(f);h.equals(d)&&(d=h.getNext())}else if(f)if(f>=h.getChildCount()){h=h.append(a.document.createText(""));e=true}else h=h.getChild(f).getPrevious();else{h=h.append(a.document.createText(""),1);e=true}var f=h.getParents(),k=d.getParents(),l,i,q;for(l=0;l<f.length;l++){i=f[l];q=k[l];if(!i.equals(q))break}for(var m=c,s,A,v,o=l;o<f.length;o++){s=f[o];m&&!s.equals(h)&&(A=m.append(s.clone()));for(s=s.getNext();s;){if(s.equals(k[o])||s.equals(d))break;v=s.getNext();
|
||||
if(b==2)m.append(s.clone(true));else{s.remove();b==1&&m.append(s)}s=v}m&&(m=A)}m=c;for(c=l;c<k.length;c++){s=k[c];b>0&&!s.equals(d)&&(A=m.append(s.clone()));if(!f[c]||s.$.parentNode!=f[c].$.parentNode)for(s=s.getPrevious();s;){if(s.equals(f[c])||s.equals(h))break;v=s.getPrevious();if(b==2)m.$.insertBefore(s.$.cloneNode(true),m.$.firstChild);else{s.remove();b==1&&m.$.insertBefore(s.$,m.$.firstChild)}s=v}m&&(m=A)}if(b==2){i=a.startContainer;if(i.type==CKEDITOR.NODE_TEXT){i.$.data=i.$.data+i.$.nextSibling.data;
|
||||
i.$.parentNode.removeChild(i.$.nextSibling)}a=a.endContainer;if(a.type==CKEDITOR.NODE_TEXT&&a.$.nextSibling){a.$.data=a.$.data+a.$.nextSibling.data;a.$.parentNode.removeChild(a.$.nextSibling)}}else{if(i&&q&&(h.$.parentNode!=i.$.parentNode||d.$.parentNode!=q.$.parentNode)){b=q.getIndex();e&&q.$.parentNode==h.$.parentNode&&b--;if(g&&i.type==CKEDITOR.NODE_ELEMENT){g=CKEDITOR.dom.element.createFromHtml('<span data-cke-bookmark="1" style="display:none"> </span>',a.document);g.insertAfter(i);i.mergeSiblings(false);
|
||||
a.moveToBookmark({startNode:g})}else a.setStart(q.getParent(),b)}a.collapse(true)}e&&h.remove();j&&d.$.parentNode&&d.remove()},d={abbr:1,acronym:1,b:1,bdo:1,big:1,cite:1,code:1,del:1,dfn:1,em:1,font:1,i:1,ins:1,label:1,kbd:1,q:1,samp:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,tt:1,u:1,"var":1},j=CKEDITOR.dom.walker.bogus(),l=new CKEDITOR.dom.walker.whitespaces,i=new CKEDITOR.dom.walker.bookmark,m=/^[\t\r\n ]*(?: |\xa0)$/;CKEDITOR.dom.range.prototype={clone:function(){var a=new CKEDITOR.dom.range(this.root);
|
||||
a.startContainer=this.startContainer;a.startOffset=this.startOffset;a.endContainer=this.endContainer;a.endOffset=this.endOffset;a.collapsed=this.collapsed;return a},collapse:function(a){if(a){this.endContainer=this.startContainer;this.endOffset=this.startOffset}else{this.startContainer=this.endContainer;this.startOffset=this.endOffset}this.collapsed=true},cloneContents:function(){var a=new CKEDITOR.dom.documentFragment(this.document);this.collapsed||e(this,2,a);return a},deleteContents:function(a){this.collapsed||
|
||||
e(this,0,null,a)},extractContents:function(a){var b=new CKEDITOR.dom.documentFragment(this.document);this.collapsed||e(this,1,b,a);return b},createBookmark:function(a){var b,c,g,h,d=this.collapsed;b=this.document.createElement("span");b.data("cke-bookmark",1);b.setStyle("display","none");b.setHtml(" ");if(a){g="cke_bm_"+CKEDITOR.tools.getNextNumber();b.setAttribute("id",g+(d?"C":"S"))}if(!d){c=b.clone();c.setHtml(" ");a&&c.setAttribute("id",g+"E");h=this.clone();h.collapse();h.insertNode(c)}h=
|
||||
this.clone();h.collapse(true);h.insertNode(b);if(c){this.setStartAfter(b);this.setEndBefore(c)}else this.moveToPosition(b,CKEDITOR.POSITION_AFTER_END);return{startNode:a?g+(d?"C":"S"):b,endNode:a?g+"E":c,serializable:a,collapsed:d}},createBookmark2:function(a){var b=this.startContainer,c=this.endContainer,g=this.startOffset,h=this.endOffset,d=this.collapsed,f,k;if(!b||!c)return{start:0,end:0};if(a){if(b.type==CKEDITOR.NODE_ELEMENT){if((f=b.getChild(g))&&f.type==CKEDITOR.NODE_TEXT&&g>0&&f.getPrevious().type==
|
||||
CKEDITOR.NODE_TEXT){b=f;g=0}f&&f.type==CKEDITOR.NODE_ELEMENT&&(g=f.getIndex(1))}for(;b.type==CKEDITOR.NODE_TEXT&&(k=b.getPrevious())&&k.type==CKEDITOR.NODE_TEXT;){b=k;g=g+k.getLength()}if(!d){if(c.type==CKEDITOR.NODE_ELEMENT){if((f=c.getChild(h))&&f.type==CKEDITOR.NODE_TEXT&&h>0&&f.getPrevious().type==CKEDITOR.NODE_TEXT){c=f;h=0}f&&f.type==CKEDITOR.NODE_ELEMENT&&(h=f.getIndex(1))}for(;c.type==CKEDITOR.NODE_TEXT&&(k=c.getPrevious())&&k.type==CKEDITOR.NODE_TEXT;){c=k;h=h+k.getLength()}}}return{start:b.getAddress(a),
|
||||
end:d?null:c.getAddress(a),startOffset:g,endOffset:h,normalized:a,collapsed:d,is2:true}},moveToBookmark:function(a){if(a.is2){var b=this.document.getByAddress(a.start,a.normalized),c=a.startOffset,g=a.end&&this.document.getByAddress(a.end,a.normalized),a=a.endOffset;this.setStart(b,c);g?this.setEnd(g,a):this.collapse(true)}else{b=(c=a.serializable)?this.document.getById(a.startNode):a.startNode;a=c?this.document.getById(a.endNode):a.endNode;this.setStartBefore(b);b.remove();if(a){this.setEndBefore(a);
|
||||
a.remove()}else this.collapse(true)}},getBoundaryNodes:function(){var a=this.startContainer,b=this.endContainer,c=this.startOffset,g=this.endOffset,h;if(a.type==CKEDITOR.NODE_ELEMENT){h=a.getChildCount();if(h>c)a=a.getChild(c);else if(h<1)a=a.getPreviousSourceNode();else{for(a=a.$;a.lastChild;)a=a.lastChild;a=new CKEDITOR.dom.node(a);a=a.getNextSourceNode()||a}}if(b.type==CKEDITOR.NODE_ELEMENT){h=b.getChildCount();if(h>g)b=b.getChild(g).getPreviousSourceNode(true);else if(h<1)b=b.getPreviousSourceNode();
|
||||
else{for(b=b.$;b.lastChild;)b=b.lastChild;b=new CKEDITOR.dom.node(b)}}a.getPosition(b)&CKEDITOR.POSITION_FOLLOWING&&(a=b);return{startNode:a,endNode:b}},getCommonAncestor:function(a,b){var c=this.startContainer,g=this.endContainer,c=c.equals(g)?a&&c.type==CKEDITOR.NODE_ELEMENT&&this.startOffset==this.endOffset-1?c.getChild(this.startOffset):c:c.getCommonAncestor(g);return b&&!c.is?c.getParent():c},optimize:function(){var a=this.startContainer,b=this.startOffset;a.type!=CKEDITOR.NODE_ELEMENT&&(b?b>=
|
||||
a.getLength()&&this.setStartAfter(a):this.setStartBefore(a));a=this.endContainer;b=this.endOffset;a.type!=CKEDITOR.NODE_ELEMENT&&(b?b>=a.getLength()&&this.setEndAfter(a):this.setEndBefore(a))},optimizeBookmark:function(){var a=this.startContainer,b=this.endContainer;a.is&&(a.is("span")&&a.data("cke-bookmark"))&&this.setStartAt(a,CKEDITOR.POSITION_BEFORE_START);b&&(b.is&&b.is("span")&&b.data("cke-bookmark"))&&this.setEndAt(b,CKEDITOR.POSITION_AFTER_END)},trim:function(a,b){var c=this.startContainer,
|
||||
g=this.startOffset,h=this.collapsed;if((!a||h)&&c&&c.type==CKEDITOR.NODE_TEXT){if(g)if(g>=c.getLength()){g=c.getIndex()+1;c=c.getParent()}else{var d=c.split(g),g=c.getIndex()+1,c=c.getParent();if(this.startContainer.equals(this.endContainer))this.setEnd(d,this.endOffset-this.startOffset);else if(c.equals(this.endContainer))this.endOffset=this.endOffset+1}else{g=c.getIndex();c=c.getParent()}this.setStart(c,g);if(h){this.collapse(true);return}}c=this.endContainer;g=this.endOffset;if(!b&&!h&&c&&c.type==
|
||||
CKEDITOR.NODE_TEXT){if(g){g>=c.getLength()||c.split(g);g=c.getIndex()+1}else g=c.getIndex();c=c.getParent();this.setEnd(c,g)}},enlarge:function(a,b){switch(a){case CKEDITOR.ENLARGE_INLINE:var c=1;case CKEDITOR.ENLARGE_ELEMENT:if(this.collapsed)break;var g=this.getCommonAncestor(),h=this.root,d,f,k,e,j,l=false,i,q;i=this.startContainer;q=this.startOffset;if(i.type==CKEDITOR.NODE_TEXT){if(q){i=!CKEDITOR.tools.trim(i.substring(0,q)).length&&i;l=!!i}if(i&&!(e=i.getPrevious()))k=i.getParent()}else{q&&
|
||||
(e=i.getChild(q-1)||i.getLast());e||(k=i)}for(;k||e;){if(k&&!e){!j&&k.equals(g)&&(j=true);if(c?k.isBlockBoundary():!h.contains(k))break;if(!l||k.getComputedStyle("display")!="inline"){l=false;j?d=k:this.setStartBefore(k)}e=k.getPrevious()}for(;e;){i=false;if(e.type==CKEDITOR.NODE_COMMENT)e=e.getPrevious();else{if(e.type==CKEDITOR.NODE_TEXT){q=e.getText();/[^\s\ufeff]/.test(q)&&(e=null);i=/[\s\ufeff]$/.test(q)}else if((e.$.offsetWidth>0||b&&e.is("br"))&&!e.data("cke-bookmark"))if(l&&CKEDITOR.dtd.$removeEmpty[e.getName()]){q=
|
||||
e.getText();if(/[^\s\ufeff]/.test(q))e=null;else for(var m=e.$.getElementsByTagName("*"),s=0,A;A=m[s++];)if(!CKEDITOR.dtd.$removeEmpty[A.nodeName.toLowerCase()]){e=null;break}e&&(i=!!q.length)}else e=null;i&&(l?j?d=k:k&&this.setStartBefore(k):l=true);if(e){i=e.getPrevious();if(!k&&!i){k=e;e=null;break}e=i}else k=null}}k&&(k=k.getParent())}i=this.endContainer;q=this.endOffset;k=e=null;j=l=false;if(i.type==CKEDITOR.NODE_TEXT){i=!CKEDITOR.tools.trim(i.substring(q)).length&&i;l=!(i&&i.getLength());if(i&&
|
||||
!(e=i.getNext()))k=i.getParent()}else(e=i.getChild(q))||(k=i);for(;k||e;){if(k&&!e){!j&&k.equals(g)&&(j=true);if(c?k.isBlockBoundary():!h.contains(k))break;if(!l||k.getComputedStyle("display")!="inline"){l=false;j?f=k:k&&this.setEndAfter(k)}e=k.getNext()}for(;e;){i=false;if(e.type==CKEDITOR.NODE_TEXT){q=e.getText();/[^\s\ufeff]/.test(q)&&(e=null);i=/^[\s\ufeff]/.test(q)}else if(e.type==CKEDITOR.NODE_ELEMENT){if((e.$.offsetWidth>0||b&&e.is("br"))&&!e.data("cke-bookmark"))if(l&&CKEDITOR.dtd.$removeEmpty[e.getName()]){q=
|
||||
e.getText();if(/[^\s\ufeff]/.test(q))e=null;else{m=e.$.getElementsByTagName("*");for(s=0;A=m[s++];)if(!CKEDITOR.dtd.$removeEmpty[A.nodeName.toLowerCase()]){e=null;break}}e&&(i=!!q.length)}else e=null}else i=1;i&&l&&(j?f=k:this.setEndAfter(k));if(e){i=e.getNext();if(!k&&!i){k=e;e=null;break}e=i}else k=null}k&&(k=k.getParent())}if(d&&f){g=d.contains(f)?f:d;this.setStartBefore(g);this.setEndAfter(g)}break;case CKEDITOR.ENLARGE_BLOCK_CONTENTS:case CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS:k=new CKEDITOR.dom.range(this.root);
|
||||
h=this.root;k.setStartAt(h,CKEDITOR.POSITION_AFTER_START);k.setEnd(this.startContainer,this.startOffset);k=new CKEDITOR.dom.walker(k);var v,o,x=CKEDITOR.dom.walker.blockBoundary(a==CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS?{br:1}:null),I=function(a){var b=x(a);b||(v=a);return b},c=function(a){var b=I(a);!b&&(a.is&&a.is("br"))&&(o=a);return b};k.guard=I;k=k.lastBackward();v=v||h;this.setStartAt(v,!v.is("br")&&(!k&&this.checkStartOfBlock()||k&&v.contains(k))?CKEDITOR.POSITION_AFTER_START:CKEDITOR.POSITION_AFTER_END);
|
||||
if(a==CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS){k=this.clone();k=new CKEDITOR.dom.walker(k);var G=CKEDITOR.dom.walker.whitespaces(),C=CKEDITOR.dom.walker.bookmark();k.evaluator=function(a){return!G(a)&&!C(a)};if((k=k.previous())&&k.type==CKEDITOR.NODE_ELEMENT&&k.is("br"))break}k=this.clone();k.collapse();k.setEndAt(h,CKEDITOR.POSITION_BEFORE_END);k=new CKEDITOR.dom.walker(k);k.guard=a==CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS?c:I;v=null;k=k.lastForward();v=v||h;this.setEndAt(v,!k&&this.checkEndOfBlock()||k&&
|
||||
v.contains(k)?CKEDITOR.POSITION_BEFORE_END:CKEDITOR.POSITION_BEFORE_START);o&&this.setEndAfter(o)}},shrink:function(a,b,c){if(!this.collapsed){var a=a||CKEDITOR.SHRINK_TEXT,g=this.clone(),h=this.startContainer,d=this.endContainer,e=this.startOffset,f=this.endOffset,j=1,i=1;if(h&&h.type==CKEDITOR.NODE_TEXT)if(e)if(e>=h.getLength())g.setStartAfter(h);else{g.setStartBefore(h);j=0}else g.setStartBefore(h);if(d&&d.type==CKEDITOR.NODE_TEXT)if(f)if(f>=d.getLength())g.setEndAfter(d);else{g.setEndAfter(d);
|
||||
i=0}else g.setEndBefore(d);var g=new CKEDITOR.dom.walker(g),l=CKEDITOR.dom.walker.bookmark();g.evaluator=function(b){return b.type==(a==CKEDITOR.SHRINK_ELEMENT?CKEDITOR.NODE_ELEMENT:CKEDITOR.NODE_TEXT)};var m;g.guard=function(b,g){if(l(b))return true;if(a==CKEDITOR.SHRINK_ELEMENT&&b.type==CKEDITOR.NODE_TEXT||g&&b.equals(m)||c===false&&b.type==CKEDITOR.NODE_ELEMENT&&b.isBlockBoundary())return false;!g&&b.type==CKEDITOR.NODE_ELEMENT&&(m=b);return true};if(j)(h=g[a==CKEDITOR.SHRINK_ELEMENT?"lastForward":
|
||||
"next"]())&&this.setStartAt(h,b?CKEDITOR.POSITION_AFTER_START:CKEDITOR.POSITION_BEFORE_START);if(i){g.reset();(g=g[a==CKEDITOR.SHRINK_ELEMENT?"lastBackward":"previous"]())&&this.setEndAt(g,b?CKEDITOR.POSITION_BEFORE_END:CKEDITOR.POSITION_AFTER_END)}return!(!j&&!i)}},insertNode:function(a){this.optimizeBookmark();this.trim(false,true);var b=this.startContainer,c=b.getChild(this.startOffset);c?a.insertBefore(c):b.append(a);a.getParent()&&a.getParent().equals(this.endContainer)&&this.endOffset++;this.setStartBefore(a)},
|
||||
moveToPosition:function(a,b){this.setStartAt(a,b);this.collapse(true)},moveToRange:function(a){this.setStart(a.startContainer,a.startOffset);this.setEnd(a.endContainer,a.endOffset)},selectNodeContents:function(a){this.setStart(a,0);this.setEnd(a,a.type==CKEDITOR.NODE_TEXT?a.getLength():a.getChildCount())},setStart:function(a,b){if(a.type==CKEDITOR.NODE_ELEMENT&&CKEDITOR.dtd.$empty[a.getName()]){b=a.getIndex();a=a.getParent()}this.startContainer=a;this.startOffset=b;if(!this.endContainer){this.endContainer=
|
||||
a;this.endOffset=b}f(this)},setEnd:function(a,b){if(a.type==CKEDITOR.NODE_ELEMENT&&CKEDITOR.dtd.$empty[a.getName()]){b=a.getIndex()+1;a=a.getParent()}this.endContainer=a;this.endOffset=b;if(!this.startContainer){this.startContainer=a;this.startOffset=b}f(this)},setStartAfter:function(a){this.setStart(a.getParent(),a.getIndex()+1)},setStartBefore:function(a){this.setStart(a.getParent(),a.getIndex())},setEndAfter:function(a){this.setEnd(a.getParent(),a.getIndex()+1)},setEndBefore:function(a){this.setEnd(a.getParent(),
|
||||
a.getIndex())},setStartAt:function(a,b){switch(b){case CKEDITOR.POSITION_AFTER_START:this.setStart(a,0);break;case CKEDITOR.POSITION_BEFORE_END:a.type==CKEDITOR.NODE_TEXT?this.setStart(a,a.getLength()):this.setStart(a,a.getChildCount());break;case CKEDITOR.POSITION_BEFORE_START:this.setStartBefore(a);break;case CKEDITOR.POSITION_AFTER_END:this.setStartAfter(a)}f(this)},setEndAt:function(a,b){switch(b){case CKEDITOR.POSITION_AFTER_START:this.setEnd(a,0);break;case CKEDITOR.POSITION_BEFORE_END:a.type==
|
||||
CKEDITOR.NODE_TEXT?this.setEnd(a,a.getLength()):this.setEnd(a,a.getChildCount());break;case CKEDITOR.POSITION_BEFORE_START:this.setEndBefore(a);break;case CKEDITOR.POSITION_AFTER_END:this.setEndAfter(a)}f(this)},fixBlock:function(a,b){var c=this.createBookmark(),g=this.document.createElement(b);this.collapse(a);this.enlarge(CKEDITOR.ENLARGE_BLOCK_CONTENTS);this.extractContents().appendTo(g);g.trim();CKEDITOR.env.ie||g.appendBogus();this.insertNode(g);this.moveToBookmark(c);return g},splitBlock:function(a){var b=
|
||||
new CKEDITOR.dom.elementPath(this.startContainer,this.root),c=new CKEDITOR.dom.elementPath(this.endContainer,this.root),g=b.block,h=c.block,d=null;if(!b.blockLimit.equals(c.blockLimit))return null;if(a!="br"){if(!g){g=this.fixBlock(true,a);h=(new CKEDITOR.dom.elementPath(this.endContainer,this.root)).block}h||(h=this.fixBlock(false,a))}a=g&&this.checkStartOfBlock();b=h&&this.checkEndOfBlock();this.deleteContents();if(g&&g.equals(h))if(b){d=new CKEDITOR.dom.elementPath(this.startContainer,this.root);
|
||||
this.moveToPosition(h,CKEDITOR.POSITION_AFTER_END);h=null}else if(a){d=new CKEDITOR.dom.elementPath(this.startContainer,this.root);this.moveToPosition(g,CKEDITOR.POSITION_BEFORE_START);g=null}else{h=this.splitElement(g);!CKEDITOR.env.ie&&!g.is("ul","ol")&&g.appendBogus()}return{previousBlock:g,nextBlock:h,wasStartOfBlock:a,wasEndOfBlock:b,elementPath:d}},splitElement:function(a){if(!this.collapsed)return null;this.setEndAt(a,CKEDITOR.POSITION_BEFORE_END);var b=this.extractContents(),c=a.clone(false);
|
||||
b.appendTo(c);c.insertAfter(a);this.moveToPosition(a,CKEDITOR.POSITION_AFTER_END);return c},removeEmptyBlocksAtEnd:function(){function a(g){return function(a){return b(a)||(c(a)||a.type==CKEDITOR.NODE_ELEMENT&&a.isEmptyInlineRemoveable())||g.is("table")&&a.is("caption")?false:true}}var b=CKEDITOR.dom.walker.whitespaces(),c=CKEDITOR.dom.walker.bookmark(false);return function(b){for(var c=this.createBookmark(),d=this[b?"endPath":"startPath"](),e=d.block||d.blockLimit,f;e&&!e.equals(d.root)&&!e.getFirst(a(e));){f=
|
||||
e.getParent();this[b?"setEndAt":"setStartAt"](e,CKEDITOR.POSITION_AFTER_END);e.remove(1);e=f}this.moveToBookmark(c)}}(),startPath:function(){return new CKEDITOR.dom.elementPath(this.startContainer,this.root)},endPath:function(){return new CKEDITOR.dom.elementPath(this.endContainer,this.root)},checkBoundaryOfElement:function(a,b){var d=b==CKEDITOR.START,g=this.clone();g.collapse(d);g[d?"setStartAt":"setEndAt"](a,d?CKEDITOR.POSITION_AFTER_START:CKEDITOR.POSITION_BEFORE_END);g=new CKEDITOR.dom.walker(g);
|
||||
g.evaluator=c(d);return g[d?"checkBackward":"checkForward"]()},checkStartOfBlock:function(){var b=this.startContainer,c=this.startOffset;if(CKEDITOR.env.ie&&c&&b.type==CKEDITOR.NODE_TEXT){b=CKEDITOR.tools.ltrim(b.substring(0,c));m.test(b)&&this.trim(0,1)}this.trim();b=new CKEDITOR.dom.elementPath(this.startContainer,this.root);c=this.clone();c.collapse(true);c.setStartAt(b.block||b.blockLimit,CKEDITOR.POSITION_AFTER_START);b=new CKEDITOR.dom.walker(c);b.evaluator=a();return b.checkBackward()},checkEndOfBlock:function(){var b=
|
||||
this.endContainer,c=this.endOffset;if(CKEDITOR.env.ie&&b.type==CKEDITOR.NODE_TEXT){b=CKEDITOR.tools.rtrim(b.substring(c));m.test(b)&&this.trim(1,0)}this.trim();b=new CKEDITOR.dom.elementPath(this.endContainer,this.root);c=this.clone();c.collapse(false);c.setEndAt(b.block||b.blockLimit,CKEDITOR.POSITION_BEFORE_END);b=new CKEDITOR.dom.walker(c);b.evaluator=a();return b.checkForward()},getPreviousNode:function(a,b,c){var d=this.clone();d.collapse(1);d.setStartAt(c||this.root,CKEDITOR.POSITION_AFTER_START);
|
||||
c=new CKEDITOR.dom.walker(d);c.evaluator=a;c.guard=b;return c.previous()},getNextNode:function(a,b,c){var d=this.clone();d.collapse();d.setEndAt(c||this.root,CKEDITOR.POSITION_BEFORE_END);c=new CKEDITOR.dom.walker(d);c.evaluator=a;c.guard=b;return c.next()},checkReadOnly:function(){function a(b,c){for(;b;){if(b.type==CKEDITOR.NODE_ELEMENT){if(b.getAttribute("contentEditable")=="false"&&!b.data("cke-editable"))return 0;if(b.is("html")||b.getAttribute("contentEditable")=="true"&&(b.contains(c)||b.equals(c)))break}b=
|
||||
b.getParent()}return 1}return function(){var b=this.startContainer,c=this.endContainer;return!(a(b,c)&&a(c,b))}}(),moveToElementEditablePosition:function(a,c){if(a.type==CKEDITOR.NODE_ELEMENT&&!a.isEditable(false)){this.moveToPosition(a,c?CKEDITOR.POSITION_AFTER_END:CKEDITOR.POSITION_BEFORE_START);return true}for(var d=0;a;){if(a.type==CKEDITOR.NODE_TEXT){c&&this.checkEndOfBlock()&&m.test(a.getText())?this.moveToPosition(a,CKEDITOR.POSITION_BEFORE_START):this.moveToPosition(a,c?CKEDITOR.POSITION_AFTER_END:
|
||||
CKEDITOR.POSITION_BEFORE_START);d=1;break}if(a.type==CKEDITOR.NODE_ELEMENT)if(a.isEditable()){this.moveToPosition(a,c?CKEDITOR.POSITION_BEFORE_END:CKEDITOR.POSITION_AFTER_START);d=1}else c&&(a.is("br")&&this.checkEndOfBlock())&&this.moveToPosition(a,CKEDITOR.POSITION_BEFORE_START);var g=a,h=d,e=void 0;g.type==CKEDITOR.NODE_ELEMENT&&g.isEditable(false)&&(e=g[c?"getLast":"getFirst"](b));!h&&!e&&(e=g[c?"getPrevious":"getNext"](b));a=e}return!!d},moveToElementEditStart:function(a){return this.moveToElementEditablePosition(a)},
|
||||
moveToElementEditEnd:function(a){return this.moveToElementEditablePosition(a,true)},getEnclosedNode:function(){var a=this.clone();a.optimize();if(a.startContainer.type!=CKEDITOR.NODE_ELEMENT||a.endContainer.type!=CKEDITOR.NODE_ELEMENT)return null;var a=new CKEDITOR.dom.walker(a),b=CKEDITOR.dom.walker.bookmark(false,true),c=CKEDITOR.dom.walker.whitespaces(true);a.evaluator=function(a){return c(a)&&b(a)};var d=a.next();a.reset();return d&&d.equals(a.previous())?d:null},getTouchedStartNode:function(){var a=
|
||||
this.startContainer;return this.collapsed||a.type!=CKEDITOR.NODE_ELEMENT?a:a.getChild(this.startOffset)||a},getTouchedEndNode:function(){var a=this.endContainer;return this.collapsed||a.type!=CKEDITOR.NODE_ELEMENT?a:a.getChild(this.endOffset-1)||a},scrollIntoView:function(){var a=new CKEDITOR.dom.element.createFromHtml("<span> </span>",this.document),b,c,d,h=this.clone();h.optimize();if(d=h.startContainer.type==CKEDITOR.NODE_TEXT){c=h.startContainer.getText();b=h.startContainer.split(h.startOffset);
|
||||
a.insertAfter(h.startContainer)}else h.insertNode(a);a.scrollIntoView();if(d){h.startContainer.setText(c);b.remove()}a.remove()}}})();CKEDITOR.POSITION_AFTER_START=1;CKEDITOR.POSITION_BEFORE_END=2;CKEDITOR.POSITION_BEFORE_START=3;CKEDITOR.POSITION_AFTER_END=4;CKEDITOR.ENLARGE_ELEMENT=1;CKEDITOR.ENLARGE_BLOCK_CONTENTS=2;CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS=3;CKEDITOR.ENLARGE_INLINE=4;CKEDITOR.START=1;CKEDITOR.END=2;CKEDITOR.SHRINK_ELEMENT=1;CKEDITOR.SHRINK_TEXT=2;
|
||||
(function(){function a(a){if(!(arguments.length<1)){this.range=a;this.forceBrBreak=0;this.enlargeBr=1;this.enforceRealBlocks=0;this._||(this._={})}}function c(a,b,c){for(a=a.getNextSourceNode(b,null,c);!f(a);)a=a.getNextSourceNode(b,null,c);return a}var b=/^[\r\n\t ]+$/,f=CKEDITOR.dom.walker.bookmark(false,true),e=CKEDITOR.dom.walker.whitespaces(true),d=function(a){return f(a)&&e(a)};a.prototype={getNextParagraph:function(a){a=a||"p";if(!CKEDITOR.dtd[this.range.root.getName()][a])return null;var e,
|
||||
i,m,n,r,p;if(!this._.started){i=this.range.clone();i.shrink(CKEDITOR.NODE_ELEMENT,true);n=i.endContainer.hasAscendant("pre",true)||i.startContainer.hasAscendant("pre",true);i.enlarge(this.forceBrBreak&&!n||!this.enlargeBr?CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS:CKEDITOR.ENLARGE_BLOCK_CONTENTS);if(!i.collapsed){n=new CKEDITOR.dom.walker(i.clone());var g=CKEDITOR.dom.walker.bookmark(true,true);n.evaluator=g;this._.nextNode=n.next();n=new CKEDITOR.dom.walker(i.clone());n.evaluator=g;n=n.previous();this._.lastNode=
|
||||
n.getNextSourceNode(true);if(this._.lastNode&&this._.lastNode.type==CKEDITOR.NODE_TEXT&&!CKEDITOR.tools.trim(this._.lastNode.getText())&&this._.lastNode.getParent().isBlockBoundary()){g=this.range.clone();g.moveToPosition(this._.lastNode,CKEDITOR.POSITION_AFTER_END);if(g.checkEndOfBlock()){g=new CKEDITOR.dom.elementPath(g.endContainer,g.root);this._.lastNode=(g.block||g.blockLimit).getNextSourceNode(true)}}if(!this._.lastNode){this._.lastNode=this._.docEndMarker=i.document.createText("");this._.lastNode.insertAfter(n)}i=
|
||||
null}this._.started=1}g=this._.nextNode;n=this._.lastNode;for(this._.nextNode=null;g;){var h=0,u=g.hasAscendant("pre"),w=g.type!=CKEDITOR.NODE_ELEMENT,k=0;if(w)g.type==CKEDITOR.NODE_TEXT&&b.test(g.getText())&&(w=0);else{var t=g.getName();if(g.isBlockBoundary(this.forceBrBreak&&!u&&{br:1})){if(t=="br")w=1;else if(!i&&!g.getChildCount()&&t!="hr"){e=g;m=g.equals(n);break}if(i){i.setEndAt(g,CKEDITOR.POSITION_BEFORE_START);if(t!="br")this._.nextNode=g}h=1}else{if(g.getFirst()){if(!i){i=this.range.clone();
|
||||
i.setStartAt(g,CKEDITOR.POSITION_BEFORE_START)}g=g.getFirst();continue}w=1}}if(w&&!i){i=this.range.clone();i.setStartAt(g,CKEDITOR.POSITION_BEFORE_START)}m=(!h||w)&&g.equals(n);if(i&&!h)for(;!g.getNext(d)&&!m;){t=g.getParent();if(t.isBlockBoundary(this.forceBrBreak&&!u&&{br:1})){h=1;w=0;m||t.equals(n);i.setEndAt(t,CKEDITOR.POSITION_BEFORE_END);break}g=t;w=1;m=g.equals(n);k=1}w&&i.setEndAt(g,CKEDITOR.POSITION_AFTER_END);g=c(g,k,n);if((m=!g)||h&&i)break}if(!e){if(!i){this._.docEndMarker&&this._.docEndMarker.remove();
|
||||
return this._.nextNode=null}e=new CKEDITOR.dom.elementPath(i.startContainer,i.root);g=e.blockLimit;h={div:1,th:1,td:1};e=e.block;if(!e&&g&&!this.enforceRealBlocks&&h[g.getName()]&&i.checkStartOfBlock()&&i.checkEndOfBlock()&&!g.equals(i.root))e=g;else if(!e||this.enforceRealBlocks&&e.getName()=="li"){e=this.range.document.createElement(a);i.extractContents().appendTo(e);e.trim();i.insertNode(e);r=p=true}else if(e.getName()!="li"){if(!i.checkStartOfBlock()||!i.checkEndOfBlock()){e=e.clone(false);i.extractContents().appendTo(e);
|
||||
e.trim();p=i.splitBlock();r=!p.wasStartOfBlock;p=!p.wasEndOfBlock;i.insertNode(e)}}else if(!m)this._.nextNode=e.equals(n)?null:c(i.getBoundaryNodes().endNode,1,n)}if(r)(i=e.getPrevious())&&i.type==CKEDITOR.NODE_ELEMENT&&(i.getName()=="br"?i.remove():i.getLast()&&i.getLast().$.nodeName.toLowerCase()=="br"&&i.getLast().remove());if(p)(i=e.getLast())&&i.type==CKEDITOR.NODE_ELEMENT&&i.getName()=="br"&&(CKEDITOR.env.ie||i.getPrevious(f)||i.getNext(f))&&i.remove();if(!this._.nextNode)this._.nextNode=m||
|
||||
e.equals(n)||!n?null:c(e,1,n);return e}};CKEDITOR.dom.range.prototype.createIterator=function(){return new a(this)}})();
|
||||
CKEDITOR.command=function(a,c){this.uiItems=[];this.exec=function(b){if(this.state==CKEDITOR.TRISTATE_DISABLED||!this.checkAllowed())return false;this.editorFocus&&a.focus();return this.fire("exec")===false?true:c.exec.call(this,a,b)!==false};this.refresh=function(a,b){if(!this.readOnly&&a.readOnly)return true;if(this.context&&!b.isContextFor(this.context)){this.disable();return true}this.enable();return this.fire("refresh",{editor:a,path:b})===false?true:c.refresh&&c.refresh.apply(this,arguments)!==
|
||||
false};var b;this.checkAllowed=function(){return typeof b=="boolean"?b:b=a.filter.checkFeature(this)};CKEDITOR.tools.extend(this,c,{modes:{wysiwyg:1},editorFocus:1,contextSensitive:!!c.context,state:CKEDITOR.TRISTATE_DISABLED});CKEDITOR.event.call(this)};
|
||||
CKEDITOR.command.prototype={enable:function(){this.state==CKEDITOR.TRISTATE_DISABLED&&this.checkAllowed()&&this.setState(!this.preserveState||typeof this.previousState=="undefined"?CKEDITOR.TRISTATE_OFF:this.previousState)},disable:function(){this.setState(CKEDITOR.TRISTATE_DISABLED)},setState:function(a){if(this.state==a||!this.checkAllowed())return false;this.previousState=this.state;this.state=a;this.fire("state");return true},toggleState:function(){this.state==CKEDITOR.TRISTATE_OFF?this.setState(CKEDITOR.TRISTATE_ON):
|
||||
this.state==CKEDITOR.TRISTATE_ON&&this.setState(CKEDITOR.TRISTATE_OFF)}};CKEDITOR.event.implementOn(CKEDITOR.command.prototype);CKEDITOR.ENTER_P=1;CKEDITOR.ENTER_BR=2;CKEDITOR.ENTER_DIV=3;
|
||||
CKEDITOR.config={customConfig:"config.js",autoUpdateElement:!0,language:"",defaultLanguage:"en",contentsLangDirection:"",enterMode:CKEDITOR.ENTER_P,forceEnterMode:!1,shiftEnterMode:CKEDITOR.ENTER_BR,docType:"<!DOCTYPE html>",bodyId:"",bodyClass:"",fullPage:!1,height:200,extraPlugins:"",removePlugins:"",protectedSource:[],tabIndex:0,width:"",baseFloatZIndex:1E4,blockedKeystrokes:[CKEDITOR.CTRL+66,CKEDITOR.CTRL+73,CKEDITOR.CTRL+85]};
|
||||
(function(){function a(a,b,d,g,h){var f=b.name;if((g||typeof a.elements!="function"||a.elements(f))&&(!a.match||a.match(b))){if(g=!h){a:if(a.nothingRequired)g=true;else{if(h=a.requiredClasses){f=b.classes;for(g=0;g<h.length;++g)if(CKEDITOR.tools.indexOf(f,h[g])==-1){g=false;break a}}g=e(b.styles,a.requiredStyles)&&e(b.attributes,a.requiredAttributes)}g=!g}if(!g){if(!a.propertiesOnly)d.valid=true;if(!d.allAttributes)d.allAttributes=c(a.attributes,b.attributes,d.validAttributes);if(!d.allStyles)d.allStyles=
|
||||
c(a.styles,b.styles,d.validStyles);if(!d.allClasses){a=a.classes;b=b.classes;g=d.validClasses;if(a)if(a===true)b=true;else{for(var h=0,f=b.length,k;h<f;++h){k=b[h];g[k]||(g[k]=a(k))}b=false}else b=false;d.allClasses=b}}}}function c(a,b,c){if(!a)return false;if(a===true)return true;for(var d in b)c[d]||(c[d]=a(d,b[d]));return false}function b(a,b){if(!a)return false;if(a===true)return a;if(typeof a=="string"){a=q(a);return a=="*"?true:CKEDITOR.tools.convertArrayToObject(a.split(b))}if(CKEDITOR.tools.isArray(a))return a.length?
|
||||
CKEDITOR.tools.convertArrayToObject(a):false;var c={},d=0,g;for(g in a){c[g]=a[g];d++}return d?c:false}function f(b){if(b._.filterFunction)return b._.filterFunction;var c=/^cke:(object|embed|param)$/,d=/^(object|embed|param)$/;return b._.filterFunction=function(g,h,e,f,k,j,p){var o=g.name,r,l=false;if(k)g.name=o=o.replace(c,"$1");if(e=e&&e[o]){i(g);for(o=0;o<e.length;++o)u(b,g,e[o]);m(g)}if(h){var o=g.name,e=h.elements[o],w=h.generic,h={valid:false,validAttributes:{},validClasses:{},validStyles:{},
|
||||
allAttributes:false,allClasses:false,allStyles:false};if(!e&&!w){f.push(g);return true}i(g);if(e){o=0;for(r=e.length;o<r;++o)a(e[o],g,h,true,j)}if(w){o=0;for(r=w.length;o<r;++o)a(w[o],g,h,false,j)}if(!h.valid){f.push(g);return true}j=h.validAttributes;o=h.validStyles;e=h.validClasses;r=g.attributes;var w=g.styles,q=r["class"],x=r.style,t,B,D=[],s=[],F=/^data-cke-/,z=false;delete r.style;delete r["class"];if(!h.allAttributes)for(t in r)if(!j[t])if(F.test(t)){if(t!=(B=t.replace(/^data-cke-saved-/,""))&&
|
||||
!j[B]){delete r[t];z=true}}else{delete r[t];z=true}if(h.allStyles){if(x)r.style=x}else{for(t in w)o[t]?D.push(t+":"+w[t]):z=true;if(D.length)r.style=D.sort().join("; ")}if(h.allClasses)q&&(r["class"]=q);else{for(t in e)e[t]&&s.push(t);s.length&&(r["class"]=s.sort().join(" "));q&&s.length<q.split(/\s+/).length&&(z=true)}z&&(l=true);if(!p&&!n(g)){f.push(g);return true}}if(k)g.name=g.name.replace(d,"cke:$1");return l}}function e(a,b){if(!b)return true;for(var c=0;c<b.length;++c)if(!(b[c]in a))return false;
|
||||
return true}function d(a){if(!a)return{};for(var a=a.split(/\s*,\s*/).sort(),b={};a.length;)b[a.shift()]=z;return b}function j(a){for(var b,c,d,g,h={},e=1,a=q(a);b=a.match(v);){if(c=b[2]){d=l(c,"styles");g=l(c,"attrs");c=l(c,"classes")}else d=g=c=null;h["$"+e++]={elements:b[1],classes:c,styles:d,attributes:g};a=a.slice(b[0].length)}return h}function l(a,b){var c=a.match(o[b]);return c?q(c[1]):null}function i(a){if(!a.styles)a.styles=CKEDITOR.tools.parseCssText(a.attributes.style||"",1);if(!a.classes)a.classes=
|
||||
a.attributes["class"]?a.attributes["class"].split(/\s+/):[]}function m(a){var b=a.attributes,c;delete b.style;delete b["class"];if(c=CKEDITOR.tools.writeCssText(a.styles,true))b.style=c;a.classes.length&&(b["class"]=a.classes.sort().join(" "))}function n(a){switch(a.name){case "a":if(!a.children.length&&!a.attributes.name)return false;break;case "img":if(!a.attributes.src)return false}return true}function r(a){return!a?false:a===true?true:function(b){return b in a}}function p(){return new CKEDITOR.htmlParser.element("br")}
|
||||
function g(a){return a.type==CKEDITOR.NODE_ELEMENT&&(a.name=="br"||D.$block[a.name])}function h(a,b,c){var d=a.name;if(D.$empty[d]||!a.children.length)if(d=="hr"&&b=="br")a.replaceWith(p());else{a.parent&&c.push({check:"it",el:a.parent});a.remove()}else if(D.$block[d]||d=="tr")if(b=="br"){if(a.previous&&!g(a.previous)){b=p();b.insertBefore(a)}if(a.next&&!g(a.next)){b=p();b.insertAfter(a)}a.replaceWithChildren()}else{var d=a.children,h;b:{h=D[b];for(var e=0,f=d.length,k;e<f;++e){k=d[e];if(k.type==
|
||||
CKEDITOR.NODE_ELEMENT&&!h[k.name]){h=false;break b}}h=true}if(h){a.name=b;a.attributes={};c.push({check:"parent-down",el:a})}else{h=a.parent;for(var e=h.type==CKEDITOR.NODE_DOCUMENT_FRAGMENT||h.name=="body",j,f=d.length;f>0;){k=d[--f];if(e&&(k.type==CKEDITOR.NODE_TEXT||k.type==CKEDITOR.NODE_ELEMENT&&D.$inline[k.name])){if(!j){j=new CKEDITOR.htmlParser.element(b);j.insertAfter(a);c.push({check:"parent-down",el:j})}j.add(k,0)}else{j=null;k.insertAfter(a);h.type!=CKEDITOR.NODE_DOCUMENT_FRAGMENT&&(k.type==
|
||||
CKEDITOR.NODE_ELEMENT&&!D[h.name][k.name])&&c.push({check:"el-up",el:k})}}a.remove()}}else if(d=="style")a.remove();else{a.parent&&c.push({check:"it",el:a.parent});a.replaceWithChildren()}}function u(a,b,c){var d,g;for(d=0;d<c.length;++d){g=c[d];if((!g.check||a.check(g.check,false))&&(!g.left||g.left(b))){g.right(b,x);break}}}function w(a,b){var c=b.getDefinition(),d=c.attributes,g=c.styles,h,e,f,k;if(a.name!=c.element)return false;for(h in d)if(h=="class"){c=d[h].split(/\s+/);for(f=a.classes.join("|");k=
|
||||
c.pop();)if(f.indexOf(k)==-1)return false}else if(a.attributes[h]!=d[h])return false;for(e in g)if(a.styles[e]!=g[e])return false;return true}function k(a,b){var c,d;if(typeof a=="string")c=a;else if(a instanceof CKEDITOR.style)d=a;else{c=a[0];d=a[1]}return[{element:c,left:d,right:function(a,c){c.transform(a,b)}}]}function t(a){return function(b){return w(b,a)}}function F(a){return function(b,c){c[a](b)}}var D=CKEDITOR.dtd,B=CKEDITOR.tools.copy,q=CKEDITOR.tools.trim,z="cke-test";CKEDITOR.filter=function(a){this.allowedContent=
|
||||
[];this.disabled=false;this.editor=null;this.enterMode=CKEDITOR.ENTER_P;this._={rules:{},transformations:{},cachedTests:{}};if(a instanceof CKEDITOR.editor){var b=this.editor=a;this.customConfig=true;var a=b.config.allowedContent,c;if(a===true)this.disabled=true;else{if(!a)this.customConfig=false;this.enterMode=c=b.blockless?CKEDITOR.ENTER_BR:b.config.enterMode;this.allow("br "+(c==CKEDITOR.ENTER_P?"p":c==CKEDITOR.ENTER_DIV?"div":""),"default",1);this.allow(a,"config",1);this.allow(b.config.extraAllowedContent,
|
||||
"extra",1);this._.toHtmlListener=b.on("toHtml",function(a){this.applyTo(a.data.dataValue,true,a.data.dontFilter)&&b.fire("dataFiltered")},this,null,6);this._.toDataFormatListener=b.on("toDataFormat",function(a){this.applyTo(a.data.dataValue,false,true)},this,null,11)}}else{this.customConfig=false;this.allow(a,"default",1)}};CKEDITOR.filter.prototype={allow:function(a,c,d){if(this.disabled||this.customConfig&&!d||!a)return false;this._.cachedChecks={};var g,h;if(typeof a=="string")a=j(a);else if(a instanceof
|
||||
CKEDITOR.style){h=a.getDefinition();d={};a=h.attributes;d[h.element]=h={styles:h.styles,requiredStyles:h.styles&&CKEDITOR.tools.objectKeys(h.styles)};if(a){a=B(a);h.classes=a["class"]?a["class"].split(/\s+/):null;h.requiredClasses=h.classes;delete a["class"];h.attributes=a;h.requiredAttributes=a&&CKEDITOR.tools.objectKeys(a)}a=d}else if(CKEDITOR.tools.isArray(a)){for(g=0;g<a.length;++g)h=this.allow(a[g],c,d);return h}var e,d=[];for(e in a){h=a[e];h=typeof h=="boolean"?{}:typeof h=="function"?{match:h}:
|
||||
B(h);if(e.charAt(0)!="$")h.elements=e;if(c)h.featureName=c.toLowerCase();var f=h;f.elements=b(f.elements,/\s+/)||null;f.propertiesOnly=f.propertiesOnly||f.elements===true;var k=/\s*,\s*/,u=void 0;for(u in s){f[u]=b(f[u],k)||null;var o=f,p=A[u],i=b(f[A[u]],k),l=f[u],w=[],m=true,q=void 0;i?m=false:i={};for(q in l)if(q.charAt(0)=="!"){q=q.slice(1);w.push(q);i[q]=true;m=false}for(;q=w.pop();){l[q]=l["!"+q];delete l["!"+q]}o[p]=(m?false:i)||null}f.match=f.match||null;this.allowedContent.push(h);d.push(h)}c=
|
||||
this._.rules;e=c.elements||{};a=c.generic||[];h=0;for(f=d.length;h<f;++h){k=B(d[h]);u=k.classes===true||k.styles===true||k.attributes===true;o=k;p=void 0;for(p in s)o[p]=r(o[p]);i=true;for(p in A){p=A[p];o[p]=CKEDITOR.tools.objectKeys(o[p]);o[p]&&(i=false)}o.nothingRequired=i;if(k.elements===true||k.elements===null){k.elements=r(k.elements);a[u?"unshift":"push"](k)}else{o=k.elements;delete k.elements;for(g in o)if(e[g])e[g][u?"unshift":"push"](k);else e[g]=[k]}}c.elements=e;c.generic=a.length?a:null;
|
||||
return true},applyTo:function(a,b,c){var d=[],g=!c&&this._.rules,e=this._.transformations,k=f(this),j=this.editor&&this.editor.config.protectedSource,u=false;a.forEach(function(a){if(a.type==CKEDITOR.NODE_ELEMENT){if(!b||!(a.name=="span"&&~CKEDITOR.tools.objectKeys(a.attributes).join("|").indexOf("data-cke-")))k(a,g,e,d,b)&&(u=true)}else if(a.type==CKEDITOR.NODE_COMMENT&&a.value.match(/^\{cke_protected\}(?!\{C\})/)){var c;a:{var h=decodeURIComponent(a.value.replace(/^\{cke_protected\}/,""));c=[];
|
||||
var f,o,p;if(j)for(o=0;o<j.length;++o)if((p=h.match(j[o]))&&p[0].length==h.length){c=true;break a}h=CKEDITOR.htmlParser.fragment.fromHtml(h);h.children.length==1&&(f=h.children[0]).type==CKEDITOR.NODE_ELEMENT&&k(f,g,e,c,b);c=!c.length}c||d.push(a)}},null,true);d.length&&(u=true);for(var o,p,a=[],c=["p","br","div"][this.enterMode-1];o=d.pop();)o.type==CKEDITOR.NODE_ELEMENT?h(o,c,a):o.remove();for(;p=a.pop();){o=p.el;if(o.parent)switch(p.check){case "it":D.$removeEmpty[o.name]&&!o.children.length?h(o,
|
||||
c,a):n(o)||h(o,c,a);break;case "el-up":o.parent.type!=CKEDITOR.NODE_DOCUMENT_FRAGMENT&&!D[o.parent.name][o.name]&&h(o,c,a);break;case "parent-down":o.parent.type!=CKEDITOR.NODE_DOCUMENT_FRAGMENT&&!D[o.parent.name][o.name]&&h(o.parent,c,a)}}return u},checkFeature:function(a){if(this.disabled||!a)return true;a.toFeature&&(a=a.toFeature(this.editor));return!a.requiredContent||this.check(a.requiredContent)},disable:function(){this.disabled=true;this._.toHtmlListener&&this._.toHtmlListener.removeListener();
|
||||
this._.toDataFormatListener&&this._.toDataFormatListener.removeListener()},addContentForms:function(a){if(!this.disabled&&a){var b,c,d=[],h;for(b=0;b<a.length&&!h;++b){c=a[b];if((typeof c=="string"||c instanceof CKEDITOR.style)&&this.check(c))h=c}if(h){for(b=0;b<a.length;++b)d.push(k(a[b],h));this.addTransformations(d)}}},addFeature:function(a){if(this.disabled||!a)return true;a.toFeature&&(a=a.toFeature(this.editor));this.allow(a.allowedContent,a.name);this.addTransformations(a.contentTransformations);
|
||||
this.addContentForms(a.contentForms);return this.customConfig&&a.requiredContent?this.check(a.requiredContent):true},addTransformations:function(a){var b,c;if(!this.disabled&&a){var d=this._.transformations,h;for(h=0;h<a.length;++h){b=a[h];var g=void 0,e=void 0,f=void 0,k=void 0,o=void 0,j=void 0;c=[];for(e=0;e<b.length;++e){f=b[e];if(typeof f=="string"){f=f.split(/\s*:\s*/);k=f[0];o=null;j=f[1]}else{k=f.check;o=f.left;j=f.right}if(!g){g=f;g=g.element?g.element:k?k.match(/^([a-z0-9]+)/i)[0]:g.left.getDefinition().element}o instanceof
|
||||
CKEDITOR.style&&(o=t(o));c.push({check:k==g?null:k,left:o,right:typeof j=="string"?F(j):j})}b=g;d[b]||(d[b]=[]);d[b].push(c)}}},check:function(a,b,c){if(this.disabled)return true;if(CKEDITOR.tools.isArray(a)){for(var h=a.length;h--;)if(this.check(a[h],b,c))return true;return false}var g,e;if(typeof a=="string"){e=a+"<"+(b===false?"0":"1")+(c?"1":"0")+">";if(e in this._.cachedChecks)return this._.cachedChecks[e];h=j(a).$1;g=h.styles;var k=h.classes;h.name=h.elements;h.classes=k=k?k.split(/\s*,\s*/):
|
||||
[];h.styles=d(g);h.attributes=d(h.attributes);h.children=[];k.length&&(h.attributes["class"]=k.join(" "));if(g)h.attributes.style=CKEDITOR.tools.writeCssText(h.styles);g=h}else{h=a.getDefinition();g=h.styles;k=h.attributes||{};if(g){g=B(g);k.style=CKEDITOR.tools.writeCssText(g,true)}else g={};g={name:h.element,attributes:k,classes:k["class"]?k["class"].split(/\s+/):[],styles:g,children:[]}}var k=CKEDITOR.tools.clone(g),o=[],p;if(b!==false&&(p=this._.transformations[g.name])){for(h=0;h<p.length;++h)u(this,
|
||||
g,p[h]);m(g)}f(this)(k,this._.rules,b===false?false:this._.transformations,o,false,!c,!c);b=o.length>0?false:CKEDITOR.tools.objectCompare(g.attributes,k.attributes,true)?true:false;typeof a=="string"&&(this._.cachedChecks[e]=b);return b}};var s={styles:1,attributes:1,classes:1},A={styles:"requiredStyles",attributes:"requiredAttributes",classes:"requiredClasses"},v=/^([a-z0-9*\s]+)((?:\s*\{[!\w\-,\s\*]+\}\s*|\s*\[[!\w\-,\s\*]+\]\s*|\s*\([!\w\-,\s\*]+\)\s*){0,3})(?:;\s*|$)/i,o={styles:/{([^}]+)}/,attrs:/\[([^\]]+)\]/,
|
||||
classes:/\(([^\)]+)\)/},x=CKEDITOR.filter.transformationsTools={sizeToStyle:function(a){this.lengthToStyle(a,"width");this.lengthToStyle(a,"height")},sizeToAttribute:function(a){this.lengthToAttribute(a,"width");this.lengthToAttribute(a,"height")},lengthToStyle:function(a,b,c){c=c||b;if(!(c in a.styles)){var d=a.attributes[b];if(d){/^\d+$/.test(d)&&(d=d+"px");a.styles[c]=d}}delete a.attributes[b]},lengthToAttribute:function(a,b,c){c=c||b;if(!(c in a.attributes)){var d=a.styles[b],h=d&&d.match(/^(\d+)(?:\.\d*)?px$/);
|
||||
h?a.attributes[c]=h[1]:d==z&&(a.attributes[c]=z)}delete a.styles[b]},alignmentToStyle:function(a){if(!("float"in a.styles)){var b=a.attributes.align;if(b=="left"||b=="right")a.styles["float"]=b}delete a.attributes.align},alignmentToAttribute:function(a){if(!("align"in a.attributes)){var b=a.styles["float"];if(b=="left"||b=="right")a.attributes.align=b}delete a.styles["float"]},matchesStyle:w,transform:function(a,b){if(typeof b=="string")a.name=b;else{var c=b.getDefinition(),d=c.styles,h=c.attributes,
|
||||
g,e,k,f;a.name=c.element;for(g in h)if(g=="class"){c=a.classes.join("|");for(k=h[g].split(/\s+/);f=k.pop();)c.indexOf(f)==-1&&a.classes.push(f)}else a.attributes[g]=h[g];for(e in d)a.styles[e]=d[e]}}}})();
|
||||
(function(){CKEDITOR.focusManager=function(a){if(a.focusManager)return a.focusManager;this.hasFocus=false;this.currentActive=null;this._={editor:a};return this};CKEDITOR.focusManager._={blurDelay:200};CKEDITOR.focusManager.prototype={focus:function(){this._.timer&&clearTimeout(this._.timer);if(!this.hasFocus&&!this._.locked){var a=CKEDITOR.currentInstance;a&&a.focusManager.blur(1);this.hasFocus=true;(a=this._.editor.container)&&a.addClass("cke_focus");this._.editor.fire("focus")}},lock:function(){this._.locked=
|
||||
1},unlock:function(){delete this._.locked},blur:function(a){function c(){if(this.hasFocus){this.hasFocus=false;var a=this._.editor.container;a&&a.removeClass("cke_focus");this._.editor.fire("blur")}}if(!this._.locked){this._.timer&&clearTimeout(this._.timer);var b=CKEDITOR.focusManager._.blurDelay;a||!b?c.call(this):this._.timer=CKEDITOR.tools.setTimeout(function(){delete this._.timer;c.call(this)},b,this)}},add:function(a,c){var b=a.getCustomData("focusmanager");if(!b||b!=this){b&&b.remove(a);var b=
|
||||
"focus",f="blur";if(c)if(CKEDITOR.env.ie){b="focusin";f="focusout"}else CKEDITOR.event.useCapture=1;var e={blur:function(){a.equals(this.currentActive)&&this.blur()},focus:function(){this.currentActive=a;this.focus()}};a.on(b,e.focus,this);a.on(f,e.blur,this);if(c)CKEDITOR.event.useCapture=0;a.setCustomData("focusmanager",this);a.setCustomData("focusmanager_handlers",e)}},remove:function(a){a.removeCustomData("focusmanager");var c=a.removeCustomData("focusmanager_handlers");a.removeListener("blur",
|
||||
c.blur);a.removeListener("focus",c.focus)}}})();CKEDITOR.keystrokeHandler=function(a){if(a.keystrokeHandler)return a.keystrokeHandler;this.keystrokes={};this.blockedKeystrokes={};this._={editor:a};return this};
|
||||
(function(){var a,c=function(b){var b=b.data,c=b.getKeystroke(),d=this.keystrokes[c],j=this._.editor;a=j.fire("key",{keyCode:c})===false;if(!a){d&&(a=j.execCommand(d,{from:"keystrokeHandler"})!==false);a||(a=!!this.blockedKeystrokes[c])}a&&b.preventDefault(true);return!a},b=function(b){if(a){a=false;b.data.preventDefault(true)}};CKEDITOR.keystrokeHandler.prototype={attach:function(a){a.on("keydown",c,this);if(CKEDITOR.env.opera||CKEDITOR.env.gecko&&CKEDITOR.env.mac)a.on("keypress",b,this)}}})();
|
||||
(function(){CKEDITOR.lang={languages:{af:1,ar:1,bg:1,bn:1,bs:1,ca:1,cs:1,cy:1,da:1,de:1,el:1,"en-au":1,"en-ca":1,"en-gb":1,en:1,eo:1,es:1,et:1,eu:1,fa:1,fi:1,fo:1,"fr-ca":1,fr:1,gl:1,gu:1,he:1,hi:1,hr:1,hu:1,is:1,it:1,ja:1,ka:1,km:1,ko:1,ku:1,lt:1,lv:1,mk:1,mn:1,ms:1,nb:1,nl:1,no:1,pl:1,"pt-br":1,pt:1,ro:1,ru:1,sk:1,sl:1,sq:1,"sr-latn":1,sr:1,sv:1,th:1,tr:1,ug:1,uk:1,vi:1,"zh-cn":1,zh:1},load:function(a,c,b){if(!a||!CKEDITOR.lang.languages[a])a=this.detect(c,a);this[a]?b(a,this[a]):CKEDITOR.scriptLoader.load(CKEDITOR.getUrl("lang/"+
|
||||
a+".js"),function(){b(a,this[a])},this)},detect:function(a,c){var b=this.languages,c=c||navigator.userLanguage||navigator.language||a,f=c.toLowerCase().match(/([a-z]+)(?:-([a-z]+))?/),e=f[1],f=f[2];b[e+"-"+f]?e=e+"-"+f:b[e]||(e=null);CKEDITOR.lang.detect=e?function(){return e}:function(a){return a};return e||a}}})();
|
||||
CKEDITOR.scriptLoader=function(){var a={},c={};return{load:function(b,f,e,d){var j=typeof b=="string";j&&(b=[b]);e||(e=CKEDITOR);var l=b.length,i=[],m=[],n=function(a){f&&(j?f.call(e,a):f.call(e,i,m))};if(l===0)n(true);else{var r=function(a,b){(b?i:m).push(a);if(--l<=0){d&&CKEDITOR.document.getDocumentElement().removeStyle("cursor");n(b)}},p=function(b,d){a[b]=1;var h=c[b];delete c[b];for(var g=0;g<h.length;g++)h[g](b,d)},g=function(b){if(a[b])r(b,true);else{var d=c[b]||(c[b]=[]);d.push(r);if(!(d.length>
|
||||
1)){var h=new CKEDITOR.dom.element("script");h.setAttributes({type:"text/javascript",src:b});if(f)if(CKEDITOR.env.ie)h.$.onreadystatechange=function(){if(h.$.readyState=="loaded"||h.$.readyState=="complete"){h.$.onreadystatechange=null;p(b,true)}};else{h.$.onload=function(){setTimeout(function(){p(b,true)},0)};h.$.onerror=function(){p(b,false)}}h.appendTo(CKEDITOR.document.getHead())}}};d&&CKEDITOR.document.getDocumentElement().setStyle("cursor","wait");for(var h=0;h<l;h++)g(b[h])}}}}();
|
||||
CKEDITOR.resourceManager=function(a,c){this.basePath=a;this.fileName=c;this.registered={};this.loaded={};this.externals={};this._={waitingList:{}}};
|
||||
CKEDITOR.resourceManager.prototype={add:function(a,c){if(this.registered[a])throw'[CKEDITOR.resourceManager.add] The resource name "'+a+'" is already registered.';var b=this.registered[a]=c||{};b.name=a;b.path=this.getPath(a);CKEDITOR.fire(a+CKEDITOR.tools.capitalize(this.fileName)+"Ready",b);return this.get(a)},get:function(a){return this.registered[a]||null},getPath:function(a){var c=this.externals[a];return CKEDITOR.getUrl(c&&c.dir||this.basePath+a+"/")},getFilePath:function(a){var c=this.externals[a];
|
||||
return CKEDITOR.getUrl(this.getPath(a)+(c&&typeof c.file=="string"?c.file:this.fileName+".js"))},addExternal:function(a,c,b){for(var a=a.split(","),f=0;f<a.length;f++)this.externals[a[f]]={dir:c,file:b}},load:function(a,c,b){CKEDITOR.tools.isArray(a)||(a=a?[a]:[]);for(var f=this.loaded,e=this.registered,d=[],j={},l={},i=0;i<a.length;i++){var m=a[i];if(m)if(!f[m]&&!e[m]){var n=this.getFilePath(m);d.push(n);n in j||(j[n]=[]);j[n].push(m)}else l[m]=this.get(m)}CKEDITOR.scriptLoader.load(d,function(a,
|
||||
d){if(d.length)throw'[CKEDITOR.resourceManager.load] Resource name "'+j[d[0]].join(",")+'" was not found at "'+d[0]+'".';for(var g=0;g<a.length;g++)for(var h=j[a[g]],e=0;e<h.length;e++){var i=h[e];l[i]=this.get(i);f[i]=1}c.call(b,l)},this)}};CKEDITOR.plugins=new CKEDITOR.resourceManager("plugins/","plugin");
|
||||
CKEDITOR.plugins.load=CKEDITOR.tools.override(CKEDITOR.plugins.load,function(a){var c={};return function(b,f,e){var d={},j=function(b){a.call(this,b,function(a){CKEDITOR.tools.extend(d,a);var b=[],l;for(l in a){var r=a[l],p=r&&r.requires;if(!c[l]){if(r.icons)for(var g=r.icons.split(","),h=0;h<g.length;h++)CKEDITOR.skin.addIcon(g[h],r.path+"icons/"+g[h]+".png");c[l]=1}if(p){p.split&&(p=p.split(","));for(r=0;r<p.length;r++)d[p[r]]||b.push(p[r])}}if(b.length)j.call(this,b);else{for(l in d){r=d[l];if(r.onLoad&&
|
||||
!r.onLoad._called){r.onLoad()===false&&delete d[l];r.onLoad._called=1}}f&&f.call(e||window,d)}},this)};j.call(this,b)}});CKEDITOR.plugins.setLang=function(a,c,b){var f=this.get(a),a=f.langEntries||(f.langEntries={}),f=f.lang||(f.lang=[]);f.split&&(f=f.split(","));CKEDITOR.tools.indexOf(f,c)==-1&&f.push(c);a[c]=b};CKEDITOR.ui=function(a){if(a.ui)return a.ui;this.items={};this.instances={};this.editor=a;this._={handlers:{}};return this};
|
||||
CKEDITOR.ui.prototype={add:function(a,c,b){b.name=a.toLowerCase();var f=this.items[a]={type:c,command:b.command||null,args:Array.prototype.slice.call(arguments,2)};CKEDITOR.tools.extend(f,b)},get:function(a){return this.instances[a]},create:function(a){var c=this.items[a],b=c&&this._.handlers[c.type],f=c&&c.command&&this.editor.getCommand(c.command),b=b&&b.create.apply(this,c.args);this.instances[a]=b;f&&f.uiItems.push(b);if(b&&!b.type)b.type=c.type;return b},addHandler:function(a,c){this._.handlers[a]=
|
||||
c},space:function(a){return CKEDITOR.document.getById(this.spaceId(a))},spaceId:function(a){return this.editor.id+"_"+a}};CKEDITOR.event.implementOn(CKEDITOR.ui);
|
||||
(function(){function a(a,d,g){CKEDITOR.event.call(this);a=a&&CKEDITOR.tools.clone(a);if(d!==void 0){if(d instanceof CKEDITOR.dom.element){if(!g)throw Error("One of the element modes must be specified.");}else throw Error("Expect element of type CKEDITOR.dom.element.");if(CKEDITOR.env.ie&&CKEDITOR.env.quirks&&g==CKEDITOR.ELEMENT_MODE_INLINE)throw Error("Inline element mode is not supported on IE quirks.");if(g==CKEDITOR.ELEMENT_MODE_INLINE&&!d.is(CKEDITOR.dtd.$editable)||g==CKEDITOR.ELEMENT_MODE_REPLACE&&
|
||||
d.is(CKEDITOR.dtd.$nonBodyContent))throw Error('The specified element mode is not supported on element: "'+d.getName()+'".');this.element=d;this.elementMode=g;this.name=this.elementMode!=CKEDITOR.ELEMENT_MODE_APPENDTO&&(d.getId()||d.getNameAtt())}else this.elementMode=CKEDITOR.ELEMENT_MODE_NONE;this._={};this.commands={};this.templates={};this.name=this.name||c();this.id=CKEDITOR.tools.getNextId();this.status="unloaded";this.config=CKEDITOR.tools.prototypedCopy(CKEDITOR.config);this.ui=new CKEDITOR.ui(this);
|
||||
this.focusManager=new CKEDITOR.focusManager(this);this.keystrokeHandler=new CKEDITOR.keystrokeHandler(this);this.on("readOnly",b);this.on("selectionChange",e);this.on("mode",b);this.on("instanceReady",function(){this.config.startupFocus&&this.focus()});CKEDITOR.fire("instanceCreated",null,this);CKEDITOR.add(this);CKEDITOR.tools.setTimeout(function(){j(this,a)},0,this)}function c(){do var a="editor"+ ++p;while(CKEDITOR.instances[a]);return a}function b(){var a=this.commands,b;for(b in a)f(this,a[b])}
|
||||
function f(a,b){b[b.startDisabled?"disable":a.readOnly&&!b.readOnly?"disable":b.modes[a.mode]?"enable":"disable"]()}function e(a){var b=this.commands,c=a.editor,d=a.data.path,g;for(g in b){a=b[g];a.contextSensitive&&a.refresh(c,d)}}function d(a){var b=a.config.customConfig;if(!b)return false;var b=CKEDITOR.getUrl(b),c=g[b]||(g[b]={});if(c.fn){c.fn.call(a,a.config);(CKEDITOR.getUrl(a.config.customConfig)==b||!d(a))&&a.fireOnce("customConfigLoaded")}else CKEDITOR.scriptLoader.load(b,function(){c.fn=
|
||||
CKEDITOR.editorConfig?CKEDITOR.editorConfig:function(){};d(a)});return true}function j(a,b){a.on("customConfigLoaded",function(){if(b){if(b.on)for(var c in b.on)a.on(c,b.on[c]);CKEDITOR.tools.extend(a.config,b,true);delete a.config.on}a.readOnly=!(!a.config.readOnly&&!(a.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?a.element.isReadOnly():a.elementMode==CKEDITOR.ELEMENT_MODE_REPLACE&&a.element.getAttribute("disabled")));a.blockless=a.elementMode==CKEDITOR.ELEMENT_MODE_INLINE&&!CKEDITOR.dtd[a.element.getName()].p;
|
||||
a.tabIndex=a.config.tabIndex||a.element&&a.element.getAttribute("tabindex")||0;if(a.config.skin)CKEDITOR.skinName=a.config.skin;a.fireOnce("configLoaded");a.dataProcessor=new CKEDITOR.htmlDataProcessor(a);a.filter=new CKEDITOR.filter(a);l(a)});if(b&&b.customConfig!=void 0)a.config.customConfig=b.customConfig;d(a)||a.fireOnce("customConfigLoaded")}function l(a){CKEDITOR.skin.loadPart("editor",function(){i(a)})}function i(a){CKEDITOR.lang.load(a.config.language,a.config.defaultLanguage,function(b,c){a.langCode=
|
||||
b;a.lang=CKEDITOR.tools.prototypedCopy(c);if(CKEDITOR.env.gecko&&CKEDITOR.env.version<10900&&a.lang.dir=="rtl")a.lang.dir="ltr";if(!a.config.contentsLangDirection)a.config.contentsLangDirection=a.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?a.element.getDirection(1):a.lang.dir;a.fire("langLoaded");m(a)})}function m(a){a.getStylesSet(function(b){a.once("loaded",function(){a.fire("stylesSet",{styles:b})},null,null,1);n(a)})}function n(a){var b=a.config,c=b.plugins,d=b.extraPlugins,g=b.removePlugins;if(d)var e=
|
||||
RegExp("(?:^|,)(?:"+d.replace(/\s*,\s*/g,"|")+")(?=,|$)","g"),c=c.replace(e,""),c=c+(","+d);if(g)var f=RegExp("(?:^|,)(?:"+g.replace(/\s*,\s*/g,"|")+")(?=,|$)","g"),c=c.replace(f,"");CKEDITOR.env.air&&(c=c+",adobeair");CKEDITOR.plugins.load(c.split(","),function(c){var d=[],g=[],e=[];a.plugins=c;for(var k in c){var j=c[k],o=j.lang,p=null,i=j.requires,r;CKEDITOR.tools.isArray(i)&&(i=i.join(","));if(i&&(r=i.match(f)))for(;i=r.pop();)CKEDITOR.tools.setTimeout(function(a,b){throw Error('Plugin "'+a.replace(",",
|
||||
"")+'" cannot be removed from the plugins list, because it\'s required by "'+b+'" plugin.');},0,null,[i,k]);if(o&&!a.lang[k]){o.split&&(o=o.split(","));if(CKEDITOR.tools.indexOf(o,a.langCode)>=0)p=a.langCode;else{p=a.langCode.replace(/-.*/,"");p=p!=a.langCode&&CKEDITOR.tools.indexOf(o,p)>=0?p:CKEDITOR.tools.indexOf(o,"en")>=0?"en":o[0]}if(!j.langEntries||!j.langEntries[p])e.push(CKEDITOR.getUrl(j.path+"lang/"+p+".js"));else{a.lang[k]=j.langEntries[p];p=null}}g.push(p);d.push(j)}CKEDITOR.scriptLoader.load(e,
|
||||
function(){for(var c=["beforeInit","init","afterInit"],e=0;e<c.length;e++)for(var k=0;k<d.length;k++){var f=d[k];e===0&&(g[k]&&f.lang&&f.langEntries)&&(a.lang[f.name]=f.langEntries[g[k]]);if(f[c[e]])f[c[e]](a)}a.fireOnce("pluginsLoaded");b.keystrokes&&a.setKeystroke(a.config.keystrokes);for(k=0;k<a.config.blockedKeystrokes.length;k++)a.keystrokeHandler.blockedKeystrokes[a.config.blockedKeystrokes[k]]=1;a.status="loaded";a.fireOnce("loaded");CKEDITOR.fire("instanceLoaded",null,a)})})}function r(){var a=
|
||||
this.element;if(a&&this.elementMode!=CKEDITOR.ELEMENT_MODE_APPENDTO){var b=this.getData();this.config.htmlEncodeOutput&&(b=CKEDITOR.tools.htmlEncode(b));a.is("textarea")?a.setValue(b):a.setHtml(b);return true}return false}a.prototype=CKEDITOR.editor.prototype;CKEDITOR.editor=a;var p=0,g={};CKEDITOR.tools.extend(CKEDITOR.editor.prototype,{addCommand:function(a,b){b.name=a.toLowerCase();var c=new CKEDITOR.command(this,b);this.mode&&f(this,c);return this.commands[a]=c},destroy:function(a){this.fire("beforeDestroy");
|
||||
!a&&r.call(this);this.editable(null);this.status="destroyed";this.fire("destroy");this.removeAllListeners();CKEDITOR.remove(this);CKEDITOR.fire("instanceDestroyed",null,this)},elementPath:function(a){return(a=a||this.getSelection().getStartElement())?new CKEDITOR.dom.elementPath(a,this.editable()):null},createRange:function(){var a=this.editable();return a?new CKEDITOR.dom.range(a):null},execCommand:function(a,b){var c=this.getCommand(a),d={name:a,commandData:b,command:c};if(c&&c.state!=CKEDITOR.TRISTATE_DISABLED&&
|
||||
this.fire("beforeCommandExec",d)!==true){d.returnValue=c.exec(d.commandData);if(!c.async&&this.fire("afterCommandExec",d)!==true)return d.returnValue}return false},getCommand:function(a){return this.commands[a]},getData:function(a){!a&&this.fire("beforeGetData");var b=this._.data;if(typeof b!="string")b=(b=this.element)&&this.elementMode==CKEDITOR.ELEMENT_MODE_REPLACE?b.is("textarea")?b.getValue():b.getHtml():"";b={dataValue:b};!a&&this.fire("getData",b);return b.dataValue},getSnapshot:function(){var a=
|
||||
this.fire("getSnapshot");if(typeof a!="string"){var b=this.element;b&&this.elementMode==CKEDITOR.ELEMENT_MODE_REPLACE&&(a=b.is("textarea")?b.getValue():b.getHtml())}return a},loadSnapshot:function(a){this.fire("loadSnapshot",a)},setData:function(a,b,c){if(b)this.on("dataReady",function(a){a.removeListener();b.call(a.editor)});a={dataValue:a};!c&&this.fire("setData",a);this._.data=a.dataValue;!c&&this.fire("afterSetData",a)},setReadOnly:function(a){a=a==void 0||a;if(this.readOnly!=a){this.readOnly=
|
||||
a;this.editable().setReadOnly(a);this.fire("readOnly")}},insertHtml:function(a,b){this.fire("insertHtml",{dataValue:a,mode:b})},insertText:function(a){this.fire("insertText",a)},insertElement:function(a){this.fire("insertElement",a)},focus:function(){this.fire("beforeFocus")},checkDirty:function(){return this.status=="ready"&&this._.previousValue!==this.getSnapshot()},resetDirty:function(){this._.previousValue=this.getSnapshot()},updateElement:function(){return r.call(this)},setKeystroke:function(){for(var a=
|
||||
this.keystrokeHandler.keystrokes,b=CKEDITOR.tools.isArray(arguments[0])?arguments[0]:[[].slice.call(arguments,0)],c,d,g=b.length;g--;){c=b[g];d=0;if(CKEDITOR.tools.isArray(c)){d=c[1];c=c[0]}d?a[c]=d:delete a[c]}},addFeature:function(a){return this.filter.addFeature(a)}})})();CKEDITOR.ELEMENT_MODE_NONE=0;CKEDITOR.ELEMENT_MODE_REPLACE=1;CKEDITOR.ELEMENT_MODE_APPENDTO=2;CKEDITOR.ELEMENT_MODE_INLINE=3;
|
||||
CKEDITOR.htmlParser=function(){this._={htmlPartsRegex:RegExp("<(?:(?:\\/([^>]+)>)|(?:!--([\\S|\\s]*?)--\>)|(?:([^\\s>]+)\\s*((?:(?:\"[^\"]*\")|(?:'[^']*')|[^\"'>])*)\\/?>))","g")}};
|
||||
(function(){var a=/([\w\-:.]+)(?:(?:\s*=\s*(?:(?:"([^"]*)")|(?:'([^']*)')|([^\s>]+)))|(?=\s|$))/g,c={checked:1,compact:1,declare:1,defer:1,disabled:1,ismap:1,multiple:1,nohref:1,noresize:1,noshade:1,nowrap:1,readonly:1,selected:1};CKEDITOR.htmlParser.prototype={onTagOpen:function(){},onTagClose:function(){},onText:function(){},onCDATA:function(){},onComment:function(){},parse:function(b){for(var f,e,d=0,j;f=this._.htmlPartsRegex.exec(b);){e=f.index;if(e>d){d=b.substring(d,e);if(j)j.push(d);else this.onText(d)}d=
|
||||
this._.htmlPartsRegex.lastIndex;if(e=f[1]){e=e.toLowerCase();if(j&&CKEDITOR.dtd.$cdata[e]){this.onCDATA(j.join(""));j=null}if(!j){this.onTagClose(e);continue}}if(j)j.push(f[0]);else if(e=f[3]){e=e.toLowerCase();if(!/="/.test(e)){var l={},i;f=f[4];var m=!!(f&&f.charAt(f.length-1)=="/");if(f)for(;i=a.exec(f);){var n=i[1].toLowerCase();i=i[2]||i[3]||i[4]||"";l[n]=!i&&c[n]?n:i}this.onTagOpen(e,l,m);!j&&CKEDITOR.dtd.$cdata[e]&&(j=[])}}else if(e=f[2])this.onComment(e)}if(b.length>d)this.onText(b.substring(d,
|
||||
b.length))}}})();
|
||||
CKEDITOR.htmlParser.basicWriter=CKEDITOR.tools.createClass({$:function(){this._={output:[]}},proto:{openTag:function(a){this._.output.push("<",a)},openTagClose:function(a,c){c?this._.output.push(" />"):this._.output.push(">")},attribute:function(a,c){typeof c=="string"&&(c=CKEDITOR.tools.htmlEncodeAttr(c));this._.output.push(" ",a,'="',c,'"')},closeTag:function(a){this._.output.push("</",a,">")},text:function(a){this._.output.push(a)},comment:function(a){this._.output.push("<\!--",a,"--\>")},write:function(a){this._.output.push(a)},
|
||||
reset:function(){this._.output=[];this._.indent=false},getHtml:function(a){var c=this._.output.join("");a&&this.reset();return c}}});"use strict";
|
||||
(function(){CKEDITOR.htmlParser.node=function(){};CKEDITOR.htmlParser.node.prototype={remove:function(){var a=this.parent.children,c=CKEDITOR.tools.indexOf(a,this),b=this.previous,f=this.next;b&&(b.next=f);f&&(f.previous=b);a.splice(c,1);this.parent=null},replaceWith:function(a){var c=this.parent.children,b=CKEDITOR.tools.indexOf(c,this),f=a.previous=this.previous,e=a.next=this.next;f&&(f.next=a);e&&(e.previous=a);c[b]=a;a.parent=this.parent;this.parent=null},insertAfter:function(a){var c=a.parent.children,
|
||||
b=CKEDITOR.tools.indexOf(c,a),f=a.next;c.splice(b+1,0,this);this.next=a.next;this.previous=a;a.next=this;f&&(f.previous=this);this.parent=a.parent},insertBefore:function(a){var c=a.parent.children,b=CKEDITOR.tools.indexOf(c,a);c.splice(b,0,this);this.next=a;(this.previous=a.previous)&&(a.previous.next=this);a.previous=this;this.parent=a.parent}}})();"use strict";CKEDITOR.htmlParser.comment=function(a){this.value=a;this._={isBlockLike:false}};
|
||||
CKEDITOR.htmlParser.comment.prototype=CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_COMMENT,filter:function(a){var c=this.value;if(!(c=a.onComment(c,this))){this.remove();return false}if(typeof c!="string"){this.replaceWith(c);return false}this.value=c;return true},writeHtml:function(a,c){c&&this.filter(c);a.comment(this.value)}});"use strict";
|
||||
(function(){CKEDITOR.htmlParser.text=function(a){this.value=a;this._={isBlockLike:false}};CKEDITOR.htmlParser.text.prototype=CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_TEXT,filter:function(a){if(!(this.value=a.onText(this.value,this))){this.remove();return false}},writeHtml:function(a,c){c&&this.filter(c);a.text(this.value)}})})();"use strict";
|
||||
(function(){CKEDITOR.htmlParser.cdata=function(a){this.value=a};CKEDITOR.htmlParser.cdata.prototype=CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_TEXT,filter:function(){},writeHtml:function(a){a.write(this.value)}})})();"use strict";CKEDITOR.htmlParser.fragment=function(){this.children=[];this.parent=null;this._={isBlockLike:true,hasInlineStarted:false}};
|
||||
(function(){function a(a){return a.name=="a"&&a.attributes.href||CKEDITOR.dtd.$removeEmpty[a.name]}var c=CKEDITOR.tools.extend({table:1,ul:1,ol:1,dl:1},CKEDITOR.dtd.table,CKEDITOR.dtd.ul,CKEDITOR.dtd.ol,CKEDITOR.dtd.dl),b={ol:1,ul:1},f=CKEDITOR.tools.extend({},{html:1},CKEDITOR.dtd.html,CKEDITOR.dtd.body,CKEDITOR.dtd.head,{style:1,script:1});CKEDITOR.htmlParser.fragment.fromHtml=function(e,d,j){function l(a){var b;if(u.length>0)for(var c=0;c<u.length;c++){var d=u[c],g=d.name,e=CKEDITOR.dtd[g],h=k.name&&
|
||||
CKEDITOR.dtd[k.name];if((!h||h[g])&&(!a||!e||e[a]||!CKEDITOR.dtd[a])){if(!b){i();b=1}d=d.clone();d.parent=k;k=d;u.splice(c,1);c--}else if(g==k.name){n(k,k.parent,1);c--}}}function i(){for(;w.length;)n(w.shift(),k)}function m(a){if(a._.isBlockLike&&a.name!="pre"&&a.name!="textarea"){var b=a.children.length,c=a.children[b-1],d;if(c&&c.type==CKEDITOR.NODE_TEXT)(d=CKEDITOR.tools.rtrim(c.value))?c.value=d:a.children.length=b-1}}function n(b,c,d){var c=c||k||h,e=k;if(b.previous===void 0){if(r(c,b)){k=c;
|
||||
g.onTagOpen(j,{});b.returnPoint=c=k}m(b);(!a(b)||b.children.length)&&c.add(b);b.name=="pre"&&(F=false);b.name=="textarea"&&(t=false)}if(b.returnPoint){k=b.returnPoint;delete b.returnPoint}else k=d?c:e}function r(a,b){if((a==h||a.name=="body")&&j&&(!a.name||CKEDITOR.dtd[a.name][j])){var c,d;return(c=b.attributes&&(d=b.attributes["data-cke-real-element-type"])?d:b.name)&&c in CKEDITOR.dtd.$inline&&!(c in CKEDITOR.dtd.head)&&!b.isOrphan||b.type==CKEDITOR.NODE_TEXT}}function p(a,b){return a in CKEDITOR.dtd.$listItem||
|
||||
a in CKEDITOR.dtd.$tableContent?a==b||a=="dt"&&b=="dd"||a=="dd"&&b=="dt":false}var g=new CKEDITOR.htmlParser,h=d instanceof CKEDITOR.htmlParser.element?d:typeof d=="string"?new CKEDITOR.htmlParser.element(d):new CKEDITOR.htmlParser.fragment,u=[],w=[],k=h,t=h.name=="textarea",F=h.name=="pre";g.onTagOpen=function(d,e,h,j){e=new CKEDITOR.htmlParser.element(d,e);if(e.isUnknown&&h)e.isEmpty=true;e.isOptionalClose=j;if(a(e))u.push(e);else{if(d=="pre")F=true;else{if(d=="br"&&F){k.add(new CKEDITOR.htmlParser.text("\n"));
|
||||
return}d=="textarea"&&(t=true)}if(d=="br")w.push(e);else{for(;;){j=(h=k.name)?CKEDITOR.dtd[h]||(k._.isBlockLike?CKEDITOR.dtd.div:CKEDITOR.dtd.span):f;if(!e.isUnknown&&!k.isUnknown&&!j[d])if(k.isOptionalClose)g.onTagClose(h);else if(d in b&&h in b){h=k.children;(h=h[h.length-1])&&h.name=="li"||n(h=new CKEDITOR.htmlParser.element("li"),k);!e.returnPoint&&(e.returnPoint=k);k=h}else if(d in CKEDITOR.dtd.$listItem&&!p(d,h))g.onTagOpen(d=="li"?"ul":"dl",{},0,1);else if(h in c&&!p(d,h)){!e.returnPoint&&
|
||||
(e.returnPoint=k);k=k.parent}else{h in CKEDITOR.dtd.$inline&&u.unshift(k);if(k.parent)n(k,k.parent,1);else{e.isOrphan=1;break}}else break}l(d);i();e.parent=k;e.isEmpty?n(e):k=e}}};g.onTagClose=function(a){for(var b=u.length-1;b>=0;b--)if(a==u[b].name){u.splice(b,1);return}for(var c=[],d=[],g=k;g!=h&&g.name!=a;){g._.isBlockLike||d.unshift(g);c.push(g);g=g.returnPoint||g.parent}if(g!=h){for(b=0;b<c.length;b++){var e=c[b];n(e,e.parent)}k=g;g._.isBlockLike&&i();n(g,g.parent);if(g==k)k=k.parent;u=u.concat(d)}a==
|
||||
"body"&&(j=false)};g.onText=function(a){if((!k._.hasInlineStarted||w.length)&&!F&&!t){a=CKEDITOR.tools.ltrim(a);if(a.length===0)return}var d=k.name,e=d?CKEDITOR.dtd[d]||(k._.isBlockLike?CKEDITOR.dtd.div:CKEDITOR.dtd.span):f;if(!t&&!e["#"]&&d in c){g.onTagOpen(d in b?"li":d=="dl"?"dd":d=="table"?"tr":d=="tr"?"td":"");g.onText(a)}else{i();l();!F&&!t&&(a=a.replace(/[\t\r\n ]{2,}|[\t\r\n]/g," "));a=new CKEDITOR.htmlParser.text(a);if(r(k,a))this.onTagOpen(j,{},0,1);k.add(a)}};g.onCDATA=function(a){k.add(new CKEDITOR.htmlParser.cdata(a))};
|
||||
g.onComment=function(a){i();l();k.add(new CKEDITOR.htmlParser.comment(a))};g.parse(e);for(i(!CKEDITOR.env.ie&&1);k!=h;)n(k,k.parent,1);m(h);return h};CKEDITOR.htmlParser.fragment.prototype={type:CKEDITOR.NODE_DOCUMENT_FRAGMENT,add:function(a,b){isNaN(b)&&(b=this.children.length);var c=b>0?this.children[b-1]:null;if(c){if(a._.isBlockLike&&c.type==CKEDITOR.NODE_TEXT){c.value=CKEDITOR.tools.rtrim(c.value);if(c.value.length===0){this.children.pop();this.add(a);return}}c.next=a}a.previous=c;a.parent=this;
|
||||
this.children.splice(b,0,a);if(!this._.hasInlineStarted)this._.hasInlineStarted=a.type==CKEDITOR.NODE_TEXT||a.type==CKEDITOR.NODE_ELEMENT&&!a._.isBlockLike},filter:function(a){a.onRoot(this);this.filterChildren(a)},filterChildren:function(a,b){if(this.childrenFilteredBy!=a.id){if(b&&!this.parent)a.onRoot(this);this.childrenFilteredBy=a.id;for(var c=0;c<this.children.length;c++)this.children[c].filter(a)===false&&c--}},writeHtml:function(a,b){b&&this.filter(b);this.writeChildrenHtml(a)},writeChildrenHtml:function(a,
|
||||
b,c){if(c&&!this.parent&&b)b.onRoot(this);b&&this.filterChildren(b);for(var b=0,c=this.children,f=c.length;b<f;b++)c[b].writeHtml(a)},forEach:function(a,b,c){!c&&(!b||this.type==b)&&a(this);for(var c=this.children,f,i=0,m=c.length;i<m;i++){f=c[i];f.type==CKEDITOR.NODE_ELEMENT?f.forEach(a,b):(!b||f.type==b)&&a(f)}}}})();
|
||||
(function(){function a(a,b){for(var c=0;a&&c<b.length;c++)var e=b[c],a=a.replace(e[0],e[1]);return a}function c(a,b,c){typeof b=="function"&&(b=[b]);var e,f;f=a.length;var n=b&&b.length;if(n){for(e=0;e<f&&a[e].pri<=c;e++);for(f=n-1;f>=0;f--)if(n=b[f]){n.pri=c;a.splice(e,0,n)}}}function b(a,b,c){if(b)for(var e in b){var m=a[e];a[e]=f(m,b[e],c);m||a.$length++}}function f(a,b,f){if(b){b.pri=f;if(a){if(a.splice)c(a,b,f);else{a=a.pri>f?[b,a]:[a,b];a.filter=e}return a}return b.filter=b}}function e(a){for(var b=
|
||||
a.type||a instanceof CKEDITOR.htmlParser.fragment,c=0;c<this.length;c++){if(b)var e=a.type,f=a.name;var n=this[c].apply(window,arguments);if(n===false)return n;if(b){if(n&&(n.name!=f||n.type!=e))return n}else if(typeof n!="string")return n;n!=void 0&&(a=n)}return a}CKEDITOR.htmlParser.filter=CKEDITOR.tools.createClass({$:function(a){this.id=CKEDITOR.tools.getNextNumber();this._={elementNames:[],attributeNames:[],elements:{$length:0},attributes:{$length:0}};a&&this.addRules(a,10)},proto:{addRules:function(a,
|
||||
e){typeof e!="number"&&(e=10);c(this._.elementNames,a.elementNames,e);c(this._.attributeNames,a.attributeNames,e);b(this._.elements,a.elements,e);b(this._.attributes,a.attributes,e);this._.text=f(this._.text,a.text,e)||this._.text;this._.comment=f(this._.comment,a.comment,e)||this._.comment;this._.root=f(this._.root,a.root,e)||this._.root},applyTo:function(a){a.filter(this)},onElementName:function(b){return a(b,this._.elementNames)},onAttributeName:function(b){return a(b,this._.attributeNames)},onText:function(a){var b=
|
||||
this._.text;return b?b.filter(a):a},onComment:function(a,b){var c=this._.comment;return c?c.filter(a,b):a},onRoot:function(a){var b=this._.root;return b?b.filter(a):a},onElement:function(a){for(var b=[this._.elements["^"],this._.elements[a.name],this._.elements.$],c,e=0;e<3;e++)if(c=b[e]){c=c.filter(a,this);if(c===false)return null;if(c&&c!=a)return this.onNode(c);if(a.parent&&!a.name)break}return a},onNode:function(a){var b=a.type;return b==CKEDITOR.NODE_ELEMENT?this.onElement(a):b==CKEDITOR.NODE_TEXT?
|
||||
new CKEDITOR.htmlParser.text(this.onText(a.value)):b==CKEDITOR.NODE_COMMENT?new CKEDITOR.htmlParser.comment(this.onComment(a.value)):null},onAttribute:function(a,b,c){if(b=this._.attributes[b]){a=b.filter(c,a,this);if(a===false)return false;if(typeof a!="undefined")return a}return c}}})})();
|
||||
(function(){function a(a,c){function g(a){return a||CKEDITOR.env.ie?new CKEDITOR.htmlParser.text(" "):new CKEDITOR.htmlParser.element("br",{"data-cke-bogus":1})}function h(a,c){return function(e){if(e.type!=CKEDITOR.NODE_DOCUMENT_FRAGMENT){var h=[],o=b(e),r,j;if(o)for(k(o,1)&&h.push(o);o;){if(d(o)&&(r=f(o))&&k(r))if((j=f(r))&&!d(j))h.push(r);else{var u=r,l=g(p),y=u.parent.children,m=CKEDITOR.tools.indexOf(y,u);y.splice(m+1,0,l);y=u.next;u.next=l;l.previous=u;l.parent=u.parent;l.next=y;i(r)}o=o.previous}for(o=
|
||||
0;o<h.length;o++)i(h[o]);if(h=CKEDITOR.env.opera&&!a||(typeof c=="function"?c(e)!==false:c))if(!p&&CKEDITOR.env.ie&&e.type==CKEDITOR.NODE_DOCUMENT_FRAGMENT)h=false;else if(!p&&CKEDITOR.env.ie&&(document.documentMode>7||e.name in CKEDITOR.dtd.tr||e.name in CKEDITOR.dtd.$listItem))h=false;else{h=b(e);h=!h||e.name=="form"&&h.name=="input"}h&&e.add(g(a))}}}function k(a,b){if((!p||!CKEDITOR.env.ie)&&a.type==CKEDITOR.NODE_ELEMENT&&a.name=="br"&&!a.attributes["data-cke-eol"])return true;var c;if(a.type==
|
||||
CKEDITOR.NODE_TEXT&&(c=a.value.match(F))){if(c.index){j(a,new CKEDITOR.htmlParser.text(a.value.substring(0,c.index)));a.value=c[0]}if(CKEDITOR.env.ie&&p&&(!b||a.parent.name in r))return true;if(!p)if((c=a.previous)&&c.name=="br"||!c||d(c))return true}return false}var o={elements:{}},p=c=="html",r=CKEDITOR.tools.extend({},z),u;for(u in r)"#"in B[u]||delete r[u];for(u in r)o.elements[u]=h(p,a.config.fillEmptyBlocks!==false);o.root=h(p);o.elements.br=function(a){return function(b){if(b.parent.type!=
|
||||
CKEDITOR.NODE_DOCUMENT_FRAGMENT){var c=b.attributes;if("data-cke-bogus"in c||"data-cke-eol"in c)delete c["data-cke-bogus"];else{for(c=b.next;c&&e(c);)c=c.next;var h=f(b);!c&&d(b.parent)?l(b.parent,g(a)):d(c)&&(h&&!d(h))&&j(c,g(a))}}}}(p);return o}function c(a){return a.enterMode!=CKEDITOR.ENTER_BR&&a.autoParagraph!==false?a.enterMode==CKEDITOR.ENTER_DIV?"div":"p":false}function b(a){for(a=a.children[a.children.length-1];a&&e(a);)a=a.previous;return a}function f(a){for(a=a.previous;a&&e(a);)a=a.previous;
|
||||
return a}function e(a){return a.type==CKEDITOR.NODE_TEXT&&!CKEDITOR.tools.trim(a.value)||a.type==CKEDITOR.NODE_ELEMENT&&a.attributes["data-cke-bookmark"]}function d(a){return a&&(a.type==CKEDITOR.NODE_ELEMENT&&a.name in z||a.type==CKEDITOR.NODE_DOCUMENT_FRAGMENT)}function j(a,b){var c=a.parent.children,g=CKEDITOR.tools.indexOf(c,a);c.splice(g,0,b);c=a.previous;a.previous=b;b.next=a;b.parent=a.parent;if(c){b.previous=c;c.next=b}}function l(a,b){var c=a.children[a.children.length-1];a.children.push(b);
|
||||
b.parent=a;if(c){c.next=b;b.previous=c}}function i(a){var b=a.parent.children,c=CKEDITOR.tools.indexOf(b,a),g=a.previous,a=a.next;g&&(g.next=a);a&&(a.previous=g);b.splice(c,1)}function m(a){var b=a.parent;return b?CKEDITOR.tools.indexOf(b.children,a):-1}function n(a){a=a.attributes;a.contenteditable!="false"&&(a["data-cke-editable"]=a.contenteditable?"true":1);a.contenteditable="false"}function r(a){a=a.attributes;switch(a["data-cke-editable"]){case "true":a.contenteditable="true";break;case "1":delete a.contenteditable}}
|
||||
function p(a){return a.replace(o,function(a,b,c){return"<"+b+c.replace(x,function(a,b){return!/^on/.test(b)&&c.indexOf("data-cke-saved-"+b)==-1?" data-cke-saved-"+a+" data-cke-"+CKEDITOR.rnd+"-"+a:a})+">"})}function g(a,b){return a.replace(b,function(a,b,c){a.indexOf("<textarea")==0&&(a=b+w(c).replace(/</g,"<").replace(/>/g,">")+"</textarea>");return"<cke:encoded>"+encodeURIComponent(a)+"</cke:encoded>"})}function h(a){return a.replace(C,function(a,b){return decodeURIComponent(b)})}function u(a){return a.replace(/<\!--(?!{cke_protected})[\s\S]+?--\>/g,
|
||||
function(a){return"<\!--"+D+"{C}"+encodeURIComponent(a).replace(/--/g,"%2D%2D")+"--\>"})}function w(a){return a.replace(/<\!--\{cke_protected\}\{C\}([\s\S]+?)--\>/g,function(a,b){return decodeURIComponent(b)})}function k(a,b){var c=b._.dataStore;return a.replace(/<\!--\{cke_protected\}([\s\S]+?)--\>/g,function(a,b){return decodeURIComponent(b)}).replace(/\{cke_protected_(\d+)\}/g,function(a,b){return c&&c[b]||""})}function t(a,b){for(var c=[],g=b.config.protectedSource,d=b._.dataStore||(b._.dataStore=
|
||||
{id:1}),e=/<\!--\{cke_temp(comment)?\}(\d*?)--\>/g,g=[/<script[\s\S]*?<\/script>/gi,/<noscript[\s\S]*?<\/noscript>/gi].concat(g),a=a.replace(/<\!--[\s\S]*?--\>/g,function(a){return"<\!--{cke_tempcomment}"+(c.push(a)-1)+"--\>"}),h=0;h<g.length;h++)a=a.replace(g[h],function(a){a=a.replace(e,function(a,b,g){return c[g]});return/cke_temp(comment)?/.test(a)?a:"<\!--{cke_temp}"+(c.push(a)-1)+"--\>"});a=a.replace(e,function(a,b,g){return"<\!--"+D+(b?"{C}":"")+encodeURIComponent(c[g]).replace(/--/g,"%2D%2D")+
|
||||
"--\>"});return a.replace(/(['"]).*?\1/g,function(a){return a.replace(/<\!--\{cke_protected\}([\s\S]+?)--\>/g,function(a,b){d[d.id]=decodeURIComponent(b);return"{cke_protected_"+d.id++ +"}"})})}CKEDITOR.htmlDataProcessor=function(b){var d,e,f=this;this.editor=b;this.dataFilter=d=new CKEDITOR.htmlParser.filter;this.htmlFilter=e=new CKEDITOR.htmlParser.filter;this.writer=new CKEDITOR.htmlParser.basicWriter;d.addRules(s);d.addRules(a(b,"data"));e.addRules(A);e.addRules(a(b,"html"));b.on("toHtml",function(a){var a=
|
||||
a.data,d=a.dataValue,d=t(d,b),d=g(d,G),d=p(d),d=g(d,I),d=d.replace(Q,"$1cke:$2"),d=d.replace(E,"<cke:$1$2></cke:$1>"),d=CKEDITOR.env.opera?d:d.replace(/(<pre\b[^>]*>)(\r\n|\n)/g,"$1$2$2"),e=a.context||b.editable().getName(),f;if(CKEDITOR.env.ie&&CKEDITOR.env.version<9&&e=="pre"){e="div";d="<pre>"+d+"</pre>";f=1}e=b.document.createElement(e);e.setHtml("a"+d);d=e.getHtml().substr(1);d=d.replace(RegExp(" data-cke-"+CKEDITOR.rnd+"-","ig")," ");f&&(d=d.replace(/^<pre>|<\/pre>$/gi,""));d=d.replace(L,"$1$2");
|
||||
d=h(d);d=w(d);a.dataValue=CKEDITOR.htmlParser.fragment.fromHtml(d,a.context,a.fixForBody===false?false:c(b.config))},null,null,5);b.on("toHtml",function(a){a.data.dataValue.filterChildren(f.dataFilter,true)},null,null,10);b.on("toHtml",function(a){var a=a.data,b=a.dataValue,c=new CKEDITOR.htmlParser.basicWriter;b.writeChildrenHtml(c);b=c.getHtml(true);a.dataValue=u(b)},null,null,15);b.on("toDataFormat",function(a){a.data.dataValue=CKEDITOR.htmlParser.fragment.fromHtml(a.data.dataValue,b.editable().getName(),
|
||||
c(b.config))},null,null,5);b.on("toDataFormat",function(a){a.data.dataValue.filterChildren(f.htmlFilter,true)},null,null,10);b.on("toDataFormat",function(a){var c=a.data.dataValue,g=f.writer;g.reset();c.writeChildrenHtml(g);c=g.getHtml(true);c=w(c);c=k(c,b);a.data.dataValue=c},null,null,15)};CKEDITOR.htmlDataProcessor.prototype={toHtml:function(a,b,c,g){var d=this.editor;!b&&b!==null&&(b=d.editable().getName());return d.fire("toHtml",{dataValue:a,context:b,fixForBody:c,dontFilter:!!g}).dataValue},
|
||||
toDataFormat:function(a){return this.editor.fire("toDataFormat",{dataValue:a}).dataValue}};var F=/(?: |\xa0)$/,D="{cke_protected}",B=CKEDITOR.dtd,q=["caption","colgroup","col","thead","tfoot","tbody"],z=CKEDITOR.tools.extend({},B.$blockLimit,B.$block),s={elements:{},attributeNames:[[/^on/,"data-cke-pa-on"]]},A={elementNames:[[/^cke:/,""],[/^\?xml:namespace$/,""]],attributeNames:[[/^data-cke-(saved|pa)-/,""],[/^data-cke-.*/,""],["hidefocus",""]],elements:{$:function(a){var b=a.attributes;if(b){if(b["data-cke-temp"])return false;
|
||||
for(var c=["name","href","src"],g,d=0;d<c.length;d++){g="data-cke-saved-"+c[d];g in b&&delete b[c[d]]}}return a},table:function(a){a.children.slice(0).sort(function(a,b){var c,g;if(a.type==CKEDITOR.NODE_ELEMENT&&b.type==a.type){c=CKEDITOR.tools.indexOf(q,a.name);g=CKEDITOR.tools.indexOf(q,b.name)}if(!(c>-1&&g>-1&&c!=g)){c=m(a);g=m(b)}return c>g?1:-1})},embed:function(a){var b=a.parent;if(b&&b.name=="object"){var c=b.attributes.width,b=b.attributes.height;c&&(a.attributes.width=c);b&&(a.attributes.height=
|
||||
b)}},param:function(a){a.children=[];a.isEmpty=true;return a},a:function(a){if(!a.children.length&&!a.attributes.name&&!a.attributes["data-cke-saved-name"])return false},span:function(a){a.attributes["class"]=="Apple-style-span"&&delete a.name},html:function(a){delete a.attributes.contenteditable;delete a.attributes["class"]},body:function(a){delete a.attributes.spellcheck;delete a.attributes.contenteditable},style:function(a){var b=a.children[0];b&&b.value&&(b.value=CKEDITOR.tools.trim(b.value));
|
||||
if(!a.attributes.type)a.attributes.type="text/css"},title:function(a){var b=a.children[0];!b&&l(a,b=new CKEDITOR.htmlParser.text);b.value=a.attributes["data-cke-title"]||""}},attributes:{"class":function(a){return CKEDITOR.tools.ltrim(a.replace(/(?:^|\s+)cke_[^\s]*/g,""))||false}}};if(CKEDITOR.env.ie)A.attributes.style=function(a){return a.replace(/(^|;)([^\:]+)/g,function(a){return a.toLowerCase()})};for(var v in{input:1,textarea:1}){s.elements[v]=n;A.elements[v]=r}var o=/<(a|area|img|input|source)\b([^>]*)>/gi,
|
||||
x=/\b(on\w+|href|src|name)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+))/gi,I=/(?:<style(?=[ >])[^>]*>[\s\S]*?<\/style>)|(?:<(:?link|meta|base)[^>]*>)/gi,G=/(<textarea(?=[ >])[^>]*>)([\s\S]*?)(?:<\/textarea>)/gi,C=/<cke:encoded>([^<]*)<\/cke:encoded>/gi,Q=/(<\/?)((?:object|embed|param|html|body|head|title)[^>]*>)/gi,L=/(<\/?)cke:((?:html|body|head|title)[^>]*>)/gi,E=/<cke:(param|embed)([^>]*?)\/?>(?!\s*<\/cke:\1)/gi})();"use strict";
|
||||
CKEDITOR.htmlParser.element=function(a,c){this.name=a;this.attributes=c||{};this.children=[];var b=a||"",f=b.match(/^cke:(.*)/);f&&(b=f[1]);b=!(!CKEDITOR.dtd.$nonBodyContent[b]&&!CKEDITOR.dtd.$block[b]&&!CKEDITOR.dtd.$listItem[b]&&!CKEDITOR.dtd.$tableContent[b]&&!(CKEDITOR.dtd.$nonEditable[b]||b=="br"));this.isEmpty=!!CKEDITOR.dtd.$empty[a];this.isUnknown=!CKEDITOR.dtd[a];this._={isBlockLike:b,hasInlineStarted:this.isEmpty||!b}};
|
||||
CKEDITOR.htmlParser.cssStyle=function(a){var c={};((a instanceof CKEDITOR.htmlParser.element?a.attributes.style:a)||"").replace(/"/g,'"').replace(/\s*([^ :;]+)\s*:\s*([^;]+)\s*(?=;|$)/g,function(a,f,e){f=="font-family"&&(e=e.replace(/["']/g,""));c[f.toLowerCase()]=e});return{rules:c,populate:function(a){var c=this.toString();if(c)a instanceof CKEDITOR.dom.element?a.setAttribute("style",c):a instanceof CKEDITOR.htmlParser.element?a.attributes.style=c:a.style=c},toString:function(){var a=[],f;
|
||||
for(f in c)c[f]&&a.push(f,":",c[f],";");return a.join("")}}};
|
||||
(function(){var a=function(a,c){a=a[0];c=c[0];return a<c?-1:a>c?1:0},c=CKEDITOR.htmlParser.fragment.prototype;CKEDITOR.htmlParser.element.prototype=CKEDITOR.tools.extend(new CKEDITOR.htmlParser.node,{type:CKEDITOR.NODE_ELEMENT,add:c.add,clone:function(){return new CKEDITOR.htmlParser.element(this.name,this.attributes)},filter:function(a){var c=this,e,d;if(!c.parent)a.onRoot(c);for(;;){e=c.name;if(!(d=a.onElementName(e))){this.remove();return false}c.name=d;if(!(c=a.onElement(c))){this.remove();return false}if(c!==
|
||||
this){this.replaceWith(c);return false}if(c.name==e)break;if(c.type!=CKEDITOR.NODE_ELEMENT){this.replaceWith(c);return false}if(!c.name){this.replaceWithChildren();return false}}e=c.attributes;var j,l;for(j in e){l=j;for(d=e[j];;)if(l=a.onAttributeName(j))if(l!=j){delete e[j];j=l}else break;else{delete e[j];break}l&&((d=a.onAttribute(c,l,d))===false?delete e[l]:e[l]=d)}c.isEmpty||this.filterChildren(a);return true},filterChildren:c.filterChildren,writeHtml:function(b,c){c&&this.filter(c);var e=this.name,
|
||||
d=[],j=this.attributes,l,i;b.openTag(e,j);for(l in j)d.push([l,j[l]]);b.sortAttributes&&d.sort(a);l=0;for(i=d.length;l<i;l++){j=d[l];b.attribute(j[0],j[1])}b.openTagClose(e,this.isEmpty);this.writeChildrenHtml(b);this.isEmpty||b.closeTag(e)},writeChildrenHtml:c.writeChildrenHtml,replaceWithChildren:function(){for(var a=this.children,c=a.length;c;)a[--c].insertAfter(this);this.remove()},forEach:c.forEach})})();
|
||||
(function(){var a={};CKEDITOR.template=function(c){if(a[c])this.output=a[c];else{var b=c.replace(/'/g,"\\'").replace(/{([^}]+)}/g,function(a,b){return"',data['"+b+"']==undefined?'{"+b+"}':data['"+b+"'],'"});this.output=a[c]=Function("data","buffer","return buffer?buffer.push('"+b+"'):['"+b+"'].join('');")}}})();delete CKEDITOR.loadFullCore;CKEDITOR.instances={};CKEDITOR.document=new CKEDITOR.dom.document(document);
|
||||
CKEDITOR.add=function(a){CKEDITOR.instances[a.name]=a;a.on("focus",function(){if(CKEDITOR.currentInstance!=a){CKEDITOR.currentInstance=a;CKEDITOR.fire("currentInstance")}});a.on("blur",function(){if(CKEDITOR.currentInstance==a){CKEDITOR.currentInstance=null;CKEDITOR.fire("currentInstance")}});CKEDITOR.fire("instance",null,a)};CKEDITOR.remove=function(a){delete CKEDITOR.instances[a.name]};
|
||||
(function(){var a={};CKEDITOR.addTemplate=function(c,b){var f=a[c];if(f)return f;f={name:c,source:b};CKEDITOR.fire("template",f);return a[c]=new CKEDITOR.template(f.source)};CKEDITOR.getTemplate=function(c){return a[c]}})();(function(){var a=[];CKEDITOR.addCss=function(c){a.push(c)};CKEDITOR.getCss=function(){return a.join("\n")}})();CKEDITOR.on("instanceDestroyed",function(){CKEDITOR.tools.isEmpty(this.instances)&&CKEDITOR.fire("reset")});CKEDITOR.TRISTATE_ON=1;CKEDITOR.TRISTATE_OFF=2;
|
||||
CKEDITOR.TRISTATE_DISABLED=0;
|
||||
(function(){CKEDITOR.inline=function(a,c){if(!CKEDITOR.env.isCompatible)return null;a=CKEDITOR.dom.element.get(a);if(a.getEditor())throw'The editor instance "'+a.getEditor().name+'" is already attached to the provided element.';var b=new CKEDITOR.editor(c,a,CKEDITOR.ELEMENT_MODE_INLINE);b.setData(a.getHtml(),null,true);b.on("loaded",function(){b.fire("uiReady");b.editable(a);b.container=a;b.setData(b.getData(1));b.resetDirty();b.fire("contentDom");b.mode="wysiwyg";b.fire("mode");b.status="ready";
|
||||
b.fireOnce("instanceReady");CKEDITOR.fire("instanceReady",null,b)},null,null,1E4);b.on("destroy",function(){b.element.clearCustomData();delete b.element});return b};CKEDITOR.inlineAll=function(){var a,c,b;for(b in CKEDITOR.dtd.$editable)for(var f=CKEDITOR.document.getElementsByTag(b),e=0,d=f.count();e<d;e++){a=f.getItem(e);if(a.getAttribute("contenteditable")=="true"){c={element:a,config:{}};CKEDITOR.fire("inline",c)!==false&&CKEDITOR.inline(a,c.config)}}};CKEDITOR.domReady(function(){!CKEDITOR.disableAutoInline&&
|
||||
CKEDITOR.inlineAll()})})();CKEDITOR.replaceClass="ckeditor";
|
||||
(function(){function a(a,e,l,i){if(!CKEDITOR.env.isCompatible)return null;a=CKEDITOR.dom.element.get(a);if(a.getEditor())throw'The editor instance "'+a.getEditor().name+'" is already attached to the provided element.';var m=new CKEDITOR.editor(e,a,i);i==CKEDITOR.ELEMENT_MODE_REPLACE&&a.setStyle("visibility","hidden");l&&m.setData(l,null,true);m.on("loaded",function(){b(m);i==CKEDITOR.ELEMENT_MODE_REPLACE&&m.config.autoUpdateElement&&f(m);m.setMode(m.config.startupMode,function(){m.resetDirty();m.status=
|
||||
"ready";m.fireOnce("instanceReady");CKEDITOR.fire("instanceReady",null,m)})});m.on("destroy",c);return m}function c(){var a=this.container,b=this.element;if(a){a.clearCustomData();a.remove()}if(b){b.clearCustomData();this.elementMode==CKEDITOR.ELEMENT_MODE_REPLACE&&b.show();delete this.element}}function b(a){var b=a.name,c=a.element,f=a.elementMode,m=a.fire("uiSpace",{space:"top",html:""}).html,n=a.fire("uiSpace",{space:"bottom",html:""}).html;e||(e=CKEDITOR.addTemplate("maincontainer",'<{outerEl} id="cke_{name}" class="{id} cke cke_reset cke_chrome cke_editor_{name} cke_{langDir} '+
|
||||
CKEDITOR.env.cssClass+'" dir="{langDir}" lang="{langCode}" role="application" aria-labelledby="cke_{name}_arialbl"><span id="cke_{name}_arialbl" class="cke_voice_label">{voiceLabel}</span><{outerEl} class="cke_inner cke_reset" role="presentation">{topHtml}<{outerEl} id="{contentId}" class="cke_contents cke_reset" role="presentation"></{outerEl}>{bottomHtml}</{outerEl}></{outerEl}>'));b=CKEDITOR.dom.element.createFromHtml(e.output({id:a.id,name:b,langDir:a.lang.dir,langCode:a.langCode,voiceLabel:a.lang.editor,
|
||||
topHtml:m?'<span id="'+a.ui.spaceId("top")+'" class="cke_top cke_reset_all" role="presentation" style="height:auto">'+m+"</span>":"",contentId:a.ui.spaceId("contents"),bottomHtml:n?'<span id="'+a.ui.spaceId("bottom")+'" class="cke_bottom cke_reset_all" role="presentation">'+n+"</span>":"",outerEl:CKEDITOR.env.ie?"span":"div"}));if(f==CKEDITOR.ELEMENT_MODE_REPLACE){c.hide();b.insertAfter(c)}else c.append(b);a.container=b;m&&a.ui.space("top").unselectable();n&&a.ui.space("bottom").unselectable();c=
|
||||
a.config.width;f=a.config.height;c&&b.setStyle("width",CKEDITOR.tools.cssLength(c));f&&a.ui.space("contents").setStyle("height",CKEDITOR.tools.cssLength(f));b.disableContextMenu();CKEDITOR.env.webkit&&b.on("focus",function(){a.focus()});a.fireOnce("uiReady")}function f(a){var b=a.element;if(a.elementMode==CKEDITOR.ELEMENT_MODE_REPLACE&&b.is("textarea")){var c=b.$.form&&new CKEDITOR.dom.element(b.$.form);if(c){var e=function(){a.updateElement()};c.on("submit",e);if(!c.$.submit.nodeName&&!c.$.submit.length)c.$.submit=
|
||||
CKEDITOR.tools.override(c.$.submit,function(b){return function(){a.updateElement();b.apply?b.apply(this,arguments):b()}});a.on("destroy",function(){c.removeListener("submit",e)})}}}CKEDITOR.replace=function(b,c){return a(b,c,null,CKEDITOR.ELEMENT_MODE_REPLACE)};CKEDITOR.appendTo=function(b,c,e){return a(b,c,e,CKEDITOR.ELEMENT_MODE_APPENDTO)};CKEDITOR.replaceAll=function(){for(var a=document.getElementsByTagName("textarea"),b=0;b<a.length;b++){var c=null,e=a[b];if(e.name||e.id){if(typeof arguments[0]==
|
||||
"string"){if(!RegExp("(?:^|\\s)"+arguments[0]+"(?:$|\\s)").test(e.className))continue}else if(typeof arguments[0]=="function"){c={};if(arguments[0](e,c)===false)continue}this.replace(e,c)}}};CKEDITOR.editor.prototype.addMode=function(a,b){(this._.modes||(this._.modes={}))[a]=b};CKEDITOR.editor.prototype.setMode=function(a,b){var c=this,e=this._.modes;if(!(a==c.mode||!e||!e[a])){c.fire("beforeSetMode",a);if(c.mode){var f=c.checkDirty();c._.previousMode=c.mode;c.fire("beforeModeUnload");c.editable(0);
|
||||
c.ui.space("contents").setHtml("");c.mode=""}this._.modes[a](function(){c.mode=a;f!==void 0&&!f&&c.resetDirty();setTimeout(function(){c.fire("mode");b&&b.call(c)},0)})}};CKEDITOR.editor.prototype.resize=function(a,b,c,e){var f=this.container,n=this.ui.space("contents"),r=CKEDITOR.env.webkit&&this.document&&this.document.getWindow().$.frameElement,e=e?f.getChild(1):f;e.setSize("width",a,true);r&&(r.style.width="1%");n.setStyle("height",Math.max(b-(c?0:(e.$.offsetHeight||0)-(n.$.clientHeight||0)),0)+
|
||||
"px");r&&(r.style.width="100%");this.fire("resize")};CKEDITOR.editor.prototype.getResizable=function(a){return a?this.ui.space("contents"):this.container};var e;CKEDITOR.domReady(function(){CKEDITOR.replaceClass&&CKEDITOR.replaceAll(CKEDITOR.replaceClass)})})();CKEDITOR.config.startupMode="wysiwyg";
|
||||
(function(){function a(a){var c=a.editor,g=c.editable(),d=a.data.path,e=d.blockLimit,f=a.data.selection.getRanges()[0],k=c.config.enterMode;if(CKEDITOR.env.gecko){var i=d.block||d.blockLimit||d.root,j=i&&i.getLast(b);i&&(i.isBlockBoundary()&&(!j||!(j.type==CKEDITOR.NODE_ELEMENT&&j.isBlockBoundary()))&&!i.is("pre")&&!i.getBogus())&&i.appendBogus()}if(c.config.autoParagraph!==false&&k!=CKEDITOR.ENTER_BR&&f.collapsed&&g.equals(e)&&!d.block){g=f.clone();g.enlarge(CKEDITOR.ENLARGE_BLOCK_CONTENTS);d=new CKEDITOR.dom.walker(g);
|
||||
d.guard=function(a){return!b(a)||a.type==CKEDITOR.NODE_COMMENT||a.isReadOnly()};if(!d.checkForward()||g.checkStartOfBlock()&&g.checkEndOfBlock()){c=f.fixBlock(true,c.config.enterMode==CKEDITOR.ENTER_DIV?"div":"p");if(CKEDITOR.env.ie)(c=c.getFirst(b))&&(c.type==CKEDITOR.NODE_TEXT&&CKEDITOR.tools.trim(c.getText()).match(/^(?: |\xa0)$/))&&c.remove();f.select();a.cancel()}}}function c(a){var b=a.data.getTarget();if(b.is("input")){b=b.getAttribute("type");(b=="submit"||b=="reset")&&a.data.preventDefault()}}
|
||||
function b(a){return i(a)&&m(a)}function f(a,b){return function(c){var d=CKEDITOR.dom.element.get(c.data.$.toElement||c.data.$.fromElement||c.data.$.relatedTarget);(!d||!b.equals(d)&&!b.contains(d))&&a.call(this,c)}}function e(a){var c,g=a.getRanges()[0],a=a.root,d=g.startPath(),e={table:1,ul:1,ol:1,dl:1},f=CKEDITOR.dom.walker.bogus();if(d.contains(e)){var k=g.clone();k.collapse(1);k.setStartAt(a,CKEDITOR.POSITION_AFTER_START);k=new CKEDITOR.dom.walker(k);d=function(a,g){return function(a,d){d&&(a.type==
|
||||
CKEDITOR.NODE_ELEMENT&&a.is(e))&&(c=a);if(b(a)&&!d&&(!g||!f(a)))return false}};k.guard=d(k);k.checkBackward();if(c){k=g.clone();k.collapse();k.setEndAt(a,CKEDITOR.POSITION_BEFORE_END);k=new CKEDITOR.dom.walker(k);k.guard=d(k,1);c=0;k.checkForward();return c}}return null}function d(a){a.editor.focus();a.editor.fire("saveSnapshot")}function j(a,b){var c=a.editor;!b&&c.getSelection().scrollIntoView();setTimeout(function(){c.fire("saveSnapshot")},0)}CKEDITOR.editable=CKEDITOR.tools.createClass({base:CKEDITOR.dom.element,
|
||||
$:function(a,b){this.base(b.$||b);this.editor=a;this.hasFocus=false;this.setup()},proto:{focus:function(){this.$[CKEDITOR.env.ie&&this.getDocument().equals(CKEDITOR.document)?"setActive":"focus"]();CKEDITOR.env.safari&&!this.isInline()&&(CKEDITOR.document.getActive().equals(this.getWindow().getFrame())||this.getWindow().focus())},on:function(a,b){var c=Array.prototype.slice.call(arguments,0);if(CKEDITOR.env.ie&&/^focus|blur$/.exec(a)){a=a=="focus"?"focusin":"focusout";b=f(b,this);c[0]=a;c[1]=b}return CKEDITOR.dom.element.prototype.on.apply(this,
|
||||
c)},attachListener:function(a,b,c,d,e,f){!this._.listeners&&(this._.listeners=[]);var k=Array.prototype.slice.call(arguments,1);this._.listeners.push(a.on.apply(a,k))},clearListeners:function(){var a=this._.listeners;try{for(;a.length;)a.pop().removeListener()}catch(b){}},restoreAttrs:function(){var a=this._.attrChanges,b,c;for(c in a)if(a.hasOwnProperty(c)){b=a[c];b!==null?this.setAttribute(c,b):this.removeAttribute(c)}},attachClass:function(a){var b=this.getCustomData("classes");if(!this.hasClass(a)){!b&&
|
||||
(b=[]);b.push(a);this.setCustomData("classes",b);this.addClass(a)}},changeAttr:function(a,b){var c=this.getAttribute(a);if(b!==c){!this._.attrChanges&&(this._.attrChanges={});a in this._.attrChanges||(this._.attrChanges[a]=c);this.setAttribute(a,b)}},insertHtml:function(a,b){d(this);n(this,b||"html",a)},insertText:function(a){d(this);var b=this.editor,c=b.getSelection().getStartElement().hasAscendant("pre",true)?CKEDITOR.ENTER_BR:b.config.enterMode,b=c==CKEDITOR.ENTER_BR,e=CKEDITOR.tools,a=e.htmlEncode(a.replace(/\r\n/g,
|
||||
"\n")),a=a.replace(/\t/g," "),c=c==CKEDITOR.ENTER_P?"p":"div";if(!b){var f=/\n{2}/g;if(f.test(a))var i="<"+c+">",k="</"+c+">",a=i+a.replace(f,function(){return k+i})+k}a=a.replace(/\n/g,"<br>");b||(a=a.replace(RegExp("<br>(?=</"+c+">)"),function(a){return e.repeat(a,2)}));a=a.replace(/^ | $/g," ");a=a.replace(/(>|\s) /g,function(a,b){return b+" "}).replace(/ (?=<)/g," ");n(this,"text",a)},insertElement:function(a){d(this);for(var c=this.editor,g=c.config.enterMode,
|
||||
e=c.getSelection(),f=e.getRanges(),i=a.getName(),k=CKEDITOR.dtd.$block[i],m,n,l,B=f.length-1;B>=0;B--){m=f[B];if(!m.checkReadOnly()){m.deleteContents(1);n=!B&&a||a.clone(1);var q,z;if(k)for(;(q=m.getCommonAncestor(0,1))&&(z=CKEDITOR.dtd[q.getName()])&&(!z||!z[i]);)if(q.getName()in CKEDITOR.dtd.span)m.splitElement(q);else if(m.checkStartOfBlock()&&m.checkEndOfBlock()){m.setStartBefore(q);m.collapse(true);q.remove()}else m.splitBlock(g==CKEDITOR.ENTER_DIV?"div":"p",c.editable());m.insertNode(n);l||
|
||||
(l=n)}}if(l){m.moveToPosition(l,CKEDITOR.POSITION_AFTER_END);if(k)if((a=l.getNext(b))&&a.type==CKEDITOR.NODE_ELEMENT&&a.is(CKEDITOR.dtd.$block))a.getDtd()["#"]?m.moveToElementEditStart(a):m.moveToElementEditEnd(l);else if(!a&&g!=CKEDITOR.ENTER_BR){a=m.fixBlock(true,g==CKEDITOR.ENTER_DIV?"div":"p");m.moveToElementEditStart(a)}}e.selectRanges([m]);j(this,CKEDITOR.env.opera)},setData:function(a,b){!b&&this.editor.dataProcessor&&(a=this.editor.dataProcessor.toHtml(a));this.setHtml(a);this.editor.fire("dataReady")},
|
||||
getData:function(a){var b=this.getHtml();!a&&this.editor.dataProcessor&&(b=this.editor.dataProcessor.toDataFormat(b));return b},setReadOnly:function(a){this.setAttribute("contenteditable",!a)},detach:function(){this.removeClass("cke_editable");var a=this.editor;this._.detach();delete a.document;delete a.window},isInline:function(){return this.getDocument().equals(CKEDITOR.document)},setup:function(){var a=this.editor;this.attachListener(a,"beforeGetData",function(){var b=this.getData();this.is("textarea")||
|
||||
a.config.ignoreEmptyParagraph!==false&&(b=b.replace(l,function(a,b){return b}));a.setData(b,null,1)},this);this.attachListener(a,"getSnapshot",function(a){a.data=this.getData(1)},this);this.attachListener(a,"afterSetData",function(){this.setData(a.getData(1))},this);this.attachListener(a,"loadSnapshot",function(a){this.setData(a.data,1)},this);this.attachListener(a,"beforeFocus",function(){var b=a.getSelection();(b=b&&b.getNative())&&b.type=="Control"||this.focus()},this);this.attachListener(a,"insertHtml",
|
||||
function(a){this.insertHtml(a.data.dataValue,a.data.mode)},this);this.attachListener(a,"insertElement",function(a){this.insertElement(a.data)},this);this.attachListener(a,"insertText",function(a){this.insertText(a.data)},this);this.setReadOnly(a.readOnly);this.attachClass("cke_editable");this.attachClass(a.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?"cke_editable_inline":a.elementMode==CKEDITOR.ELEMENT_MODE_REPLACE||a.elementMode==CKEDITOR.ELEMENT_MODE_APPENDTO?"cke_editable_themed":"");this.attachClass("cke_contents_"+
|
||||
a.config.contentsLangDirection);a.keystrokeHandler.blockedKeystrokes[8]=a.readOnly;a.keystrokeHandler.attach(this);this.on("blur",function(a){CKEDITOR.env.opera&&CKEDITOR.document.getActive().equals(this.isInline()?this:this.getWindow().getFrame())?a.cancel():this.hasFocus=false},null,null,-1);this.on("focus",function(){this.hasFocus=true},null,null,-1);a.focusManager.add(this);if(this.equals(CKEDITOR.document.getActive())){this.hasFocus=true;a.once("contentDom",function(){a.focusManager.focus()})}this.isInline()&&
|
||||
this.changeAttr("tabindex",a.tabIndex);if(!this.is("textarea")){a.document=this.getDocument();a.window=this.getWindow();var b=a.document;this.changeAttr("spellcheck",!a.config.disableNativeSpellChecker);var g=a.config.contentsLangDirection;this.getDirection(1)!=g&&this.changeAttr("dir",g);var d=CKEDITOR.getCss();if(d){g=b.getHead();if(!g.getCustomData("stylesheet")){d=b.appendStyleText(d);d=new CKEDITOR.dom.element(d.ownerNode||d.owningElement);g.setCustomData("stylesheet",d);d.data("cke-temp",1)}}g=
|
||||
b.getCustomData("stylesheet_ref")||0;b.setCustomData("stylesheet_ref",g+1);this.setCustomData("cke_includeReadonly",!a.config.disableReadonlyStyling);this.attachListener(this,"click",function(a){var a=a.data,b=a.getTarget();b.is("a")&&(a.$.button!=2&&b.isReadOnly())&&a.preventDefault()});this.attachListener(a,"key",function(b){if(a.readOnly)return true;var c=b.data.keyCode,g;if(c in{8:1,46:1}){var d=a.getSelection(),b=d.getRanges()[0],h=b.startPath(),f,p,j,c=c==8;if(d=e(d)){a.fire("saveSnapshot");
|
||||
b.moveToPosition(d,CKEDITOR.POSITION_BEFORE_START);d.remove();b.select();a.fire("saveSnapshot");g=1}else if(b.collapsed)if((f=h.block)&&b[c?"checkStartOfBlock":"checkEndOfBlock"]()&&(j=f[c?"getPrevious":"getNext"](i))&&j.is("table")){a.fire("saveSnapshot");b[c?"checkEndOfBlock":"checkStartOfBlock"]()&&f.remove();b["moveToElementEdit"+(c?"End":"Start")](j);b.select();a.fire("saveSnapshot");g=1}else if(h.blockLimit&&h.blockLimit.is("td")&&(p=h.blockLimit.getAscendant("table"))&&b.checkBoundaryOfElement(p,
|
||||
c?CKEDITOR.START:CKEDITOR.END)&&(j=p[c?"getPrevious":"getNext"](i))){a.fire("saveSnapshot");b["moveToElementEdit"+(c?"End":"Start")](j);b.checkStartOfBlock()&&b.checkEndOfBlock()?j.remove():b.select();a.fire("saveSnapshot");g=1}else if((p=h.contains(["td","th","caption"]))&&b.checkBoundaryOfElement(p,c?CKEDITOR.START:CKEDITOR.END))g=1}return!g});CKEDITOR.env.ie&&this.attachListener(this,"click",c);!CKEDITOR.env.ie&&!CKEDITOR.env.opera&&this.attachListener(this,"mousedown",function(b){var c=b.data.getTarget();
|
||||
if(c.is("img","hr","input","textarea","select")){a.getSelection().selectElement(c);c.is("input","textarea","select")&&b.data.preventDefault()}});CKEDITOR.env.gecko&&this.attachListener(this,"mouseup",function(b){if(b.data.$.button==2){b=b.data.getTarget();if(!b.getOuterHtml().replace(l,"")){var c=a.createRange();c.moveToElementEditStart(b);c.select(true)}}});if(CKEDITOR.env.webkit){this.attachListener(this,"click",function(a){a.data.getTarget().is("input","select")&&a.data.preventDefault()});this.attachListener(this,
|
||||
"mouseup",function(a){a.data.getTarget().is("input","textarea")&&a.data.preventDefault()})}}}},_:{detach:function(){this.editor.setData(this.editor.getData(),0,1);this.clearListeners();this.restoreAttrs();var a;if(a=this.removeCustomData("classes"))for(;a.length;)this.removeClass(a.pop());a=this.getDocument();var b=a.getHead();if(b.getCustomData("stylesheet")){var c=a.getCustomData("stylesheet_ref");if(--c)a.setCustomData("stylesheet_ref",c);else{a.removeCustomData("stylesheet_ref");b.removeCustomData("stylesheet").remove()}}delete this.editor}}});
|
||||
CKEDITOR.editor.prototype.editable=function(a){var b=this._.editable;if(b&&a)return 0;if(arguments.length)b=this._.editable=a?a instanceof CKEDITOR.editable?a:new CKEDITOR.editable(this,a):(b&&b.detach(),null);return b};var l=/(^|<body\b[^>]*>)\s*<(p|div|address|h\d|center|pre)[^>]*>\s*(?:<br[^>]*>| |\u00A0| )?\s*(:?<\/\2>)?\s*(?=$|<\/body>)/gi,i=CKEDITOR.dom.walker.whitespaces(true),m=CKEDITOR.dom.walker.bookmark(false,true);CKEDITOR.on("instanceLoaded",function(b){var c=b.editor;c.on("insertElement",
|
||||
function(a){a=a.data;if(a.type==CKEDITOR.NODE_ELEMENT&&(a.is("input")||a.is("textarea"))){a.getAttribute("contentEditable")!="false"&&a.data("cke-editable",a.hasAttribute("contenteditable")?"true":"1");a.setAttribute("contentEditable",false)}});c.on("selectionChange",function(b){if(!c.readOnly){var d=c.getSelection();if(d&&!d.isLocked){d=c.checkDirty();c.fire("lockSnapshot");a(b);c.fire("unlockSnapshot");!d&&c.resetDirty()}}})});CKEDITOR.on("instanceCreated",function(a){var b=a.editor;b.on("mode",
|
||||
function(){var a=b.editable();if(a&&a.isInline()){var c=this.lang.editor+", "+this.name;a.changeAttr("role","textbox");a.changeAttr("aria-label",c);a.changeAttr("title",c);if(c=this.ui.space(this.elementMode==CKEDITOR.ELEMENT_MODE_INLINE?"top":"contents")){var d=CKEDITOR.tools.getNextId(),e=CKEDITOR.dom.element.createFromHtml('<span id="'+d+'" class="cke_voice_label">'+this.lang.common.editorHelp+"</span>");c.append(e);a.changeAttr("aria-describedby",d)}}})});CKEDITOR.addCss(".cke_editable{cursor:text}.cke_editable img,.cke_editable input,.cke_editable textarea{cursor:default}");
|
||||
var n=function(){function a(b){return b.type==CKEDITOR.NODE_ELEMENT}function c(b,d){var g,e,h,f,o=[],i=d.range.startContainer;g=d.range.startPath();for(var i=k[i.getName()],j=0,m=b.getChildren(),n=m.count(),l=-1,u=-1,t=0,w=g.contains(k.$list);j<n;++j){g=m.getItem(j);if(a(g)){h=g.getName();if(w&&h in CKEDITOR.dtd.$list)o=o.concat(c(g,d));else{f=!!i[h];if(h=="br"&&g.data("cke-eol")&&(!j||j==n-1)){t=(e=j?o[j-1].node:m.getItem(j+1))&&(!a(e)||!e.is("br"));e=e&&a(e)&&k.$block[e.getName()]}l==-1&&!f&&(l=
|
||||
j);f||(u=j);o.push({isElement:1,isLineBreak:t,isBlock:g.isBlockBoundary(),hasBlockSibling:e,node:g,name:h,allowed:f});e=t=0}}else o.push({isElement:0,node:g,allowed:1})}if(l>-1)o[l].firstNotAllowed=1;if(u>-1)o[u].lastNotAllowed=1;return o}function d(b,c){var e=[],h=b.getChildren(),f=h.count(),i,o=0,j=k[c],p=!b.is(k.$inline)||b.is("br");for(p&&e.push(" ");o<f;o++){i=h.getItem(o);a(i)&&!i.is(j)?e=e.concat(d(i,c)):e.push(i)}p&&e.push(" ");return e}function e(b){return b&&a(b)&&(b.is(k.$removeEmpty)||
|
||||
b.is("a")&&!b.isBlockBoundary())}function f(b,c,d,g){var e=b.clone(),h,k;e.setEndAt(c,CKEDITOR.POSITION_BEFORE_END);if((h=(new CKEDITOR.dom.walker(e)).next())&&a(h)&&m[h.getName()]&&(k=h.getPrevious())&&a(k)&&!k.getParent().equals(b.startContainer)&&d.contains(k)&&g.contains(h)&&h.isIdentical(k)){h.moveChildren(k);h.remove();f(b,c,d,g)}}function i(b,c){function d(b,c){if(c.isBlock&&c.isElement&&!c.node.is("br")&&a(b)&&b.is("br")){b.remove();return 1}}var g=c.endContainer.getChild(c.endOffset),e=c.endContainer.getChild(c.endOffset-
|
||||
1);g&&d(g,b[b.length-1]);if(e&&d(e,b[0])){c.setEnd(c.endContainer,c.endOffset-1);c.collapse()}}var k=CKEDITOR.dtd,m={p:1,div:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,ul:1,ol:1,li:1,pre:1,dl:1,blockquote:1},l={p:1,div:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1},n=CKEDITOR.tools.extend({},k.$inline);delete n.br;return function(m,q,t){var s=m.editor;m.getDocument();var A=s.getSelection().getRanges()[0],v=false;if(q=="unfiltered_html"){q="html";v=true}if(!A.checkReadOnly()){var o=(new CKEDITOR.dom.elementPath(A.startContainer,
|
||||
A.root)).blockLimit||A.root,q={type:q,dontFilter:v,editable:m,editor:s,range:A,blockLimit:o,mergeCandidates:[],zombies:[]},s=q.range,v=q.mergeCandidates,x,I,G,C;if(q.type=="text"&&s.shrink(CKEDITOR.SHRINK_ELEMENT,true,false)){x=CKEDITOR.dom.element.createFromHtml("<span> </span>",s.document);s.insertNode(x);s.setStartAfter(x)}I=new CKEDITOR.dom.elementPath(s.startContainer);q.endPath=G=new CKEDITOR.dom.elementPath(s.endContainer);if(!s.collapsed){var o=G.block||G.blockLimit,Q=s.getCommonAncestor();
|
||||
o&&(!o.equals(Q)&&!o.contains(Q)&&s.checkEndOfBlock())&&q.zombies.push(o);s.deleteContents()}for(;(C=a(s.startContainer)&&s.startContainer.getChild(s.startOffset-1))&&a(C)&&C.isBlockBoundary()&&I.contains(C);)s.moveToPosition(C,CKEDITOR.POSITION_BEFORE_END);f(s,q.blockLimit,I,G);if(x){s.setEndBefore(x);s.collapse();x.remove()}x=s.startPath();if(o=x.contains(e,false,1)){s.splitElement(o);q.inlineStylesRoot=o;q.inlineStylesPeak=x.lastElement}x=s.createBookmark();(o=x.startNode.getPrevious(b))&&a(o)&&
|
||||
e(o)&&v.push(o);(o=x.startNode.getNext(b))&&a(o)&&e(o)&&v.push(o);for(o=x.startNode;(o=o.getParent())&&e(o);)v.push(o);s.moveToBookmark(x);if(t){C=t;t=q.range;if(q.type=="text"&&q.inlineStylesRoot){x=C;C=q.inlineStylesPeak;s=C.getDocument().createText("{cke-peak}");for(v=q.inlineStylesRoot.getParent();!C.equals(v);){s=s.appendTo(C.clone());C=C.getParent()}C=s.getOuterHtml().replace("{cke-peak}",x)}x=q.blockLimit.getName();if(/^\s+|\s+$/.test(C)&&"span"in CKEDITOR.dtd[x]){var L='<span data-cke-marker="1"> </span>';
|
||||
C=L+C+L}C=q.editor.dataProcessor.toHtml(C,null,false,q.dontFilter);x=t.document.createElement("body");x.setHtml(C);if(L){x.getFirst().remove();x.getLast().remove()}if((L=t.startPath().block)&&!(L.getChildCount()==1&&L.getBogus()))a:{var E;if(x.getChildCount()==1&&a(E=x.getFirst())&&E.is(l)){L=E.getElementsByTag("*");t=0;for(s=L.count();t<s;t++){C=L.getItem(t);if(!C.is(n))break a}E.moveChildren(E.getParent(1));E.remove()}}q.dataWrapper=x;E=q.range;var L=E.document,y,t=q.blockLimit;x=0;var J;C=[];var H,
|
||||
N,v=s=0,K,O;I=E.startContainer;var o=q.endPath.elements[0],P;G=o.getPosition(I);Q=!!o.getCommonAncestor(I)&&G!=CKEDITOR.POSITION_IDENTICAL&&!(G&CKEDITOR.POSITION_CONTAINS+CKEDITOR.POSITION_IS_CONTAINED);I=c(q.dataWrapper,q);for(i(I,E);x<I.length;x++){G=I[x];if(y=G.isLineBreak){y=E;K=t;var M=void 0,R=void 0;if(G.hasBlockSibling)y=1;else{M=y.startContainer.getAscendant(k.$block,1);if(!M||!M.is({div:1,p:1}))y=0;else{R=M.getPosition(K);if(R==CKEDITOR |