LOTS of stuff

This commit is contained in:
jomo
2014-04-04 01:08:17 +02:00
parent 0604bbce63
commit f290258f26
62 changed files with 1457 additions and 655 deletions

View File

@@ -2,7 +2,7 @@ source 'https://rubygems.org'
gem 'rails', '3.2.12' gem 'rails', '3.2.12'
gem 'sqlite3' gem 'mysql2'
gem 'jquery-rails' gem 'jquery-rails'
gem 'therubyracer' gem 'therubyracer'
gem 'bcrypt-ruby', '~> 3.0.0' # To use ActiveModel's has_secure_password gem 'bcrypt-ruby', '~> 3.0.0' # To use ActiveModel's has_secure_password

View File

@@ -67,6 +67,7 @@ GEM
treetop (~> 1.4.8) treetop (~> 1.4.8)
mime-types (1.23) mime-types (1.23)
multi_json (1.7.3) multi_json (1.7.3)
mysql2 (0.3.15)
nokogiri (1.5.9) nokogiri (1.5.9)
polyglot (0.3.3) polyglot (0.3.3)
rack (1.4.5) rack (1.4.5)
@@ -119,7 +120,6 @@ GEM
multi_json (~> 1.0) multi_json (~> 1.0)
rack (~> 1.0) rack (~> 1.0)
tilt (~> 1.1, != 1.3.0) tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.7)
therubyracer (0.11.4) therubyracer (0.11.4)
libv8 (~> 3.11.8.12) libv8 (~> 3.11.8.12)
ref ref
@@ -146,6 +146,7 @@ DEPENDENCIES
github-markdown github-markdown
hirb hirb
jquery-rails jquery-rails
mysql2
rails (= 3.2.12) rails (= 3.2.12)
rails-erd rails-erd
rb-readline (~> 0.4.2) rb-readline (~> 0.4.2)
@@ -153,7 +154,6 @@ DEPENDENCIES
sanitize sanitize
sass-rails (~> 3.2.3) sass-rails (~> 3.2.3)
simple_form simple_form
sqlite3
therubyracer therubyracer
uglifier (>= 1.0.3) uglifier (>= 1.0.3)
webrick webrick

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 B

BIN
app/assets/images/lock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 B

BIN
app/assets/images/pin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

View File

@@ -27,6 +27,7 @@ $(function(){
container: 'epic', container: 'epic',
textarea: 'epic-textarea', textarea: 'epic-textarea',
basePath: '/assets', basePath: '/assets',
clientSideStorage: false,
theme: { theme: {
base: '/base/epiceditor.css', base: '/base/epiceditor.css',
preview: '/preview/github.css', preview: '/preview/github.css',
@@ -36,7 +37,7 @@ $(function(){
bar: true bar: true
}, },
autogrow: { autogrow: {
minHeight: 500 minHeight: 300
} }
}); });
try { try {

View File

@@ -13,3 +13,4 @@
//= require jquery_ujs //= require jquery_ujs
//= require epiceditor //= require epiceditor
//= require app //= require app
//= require moment

File diff suppressed because one or more lines are too long

View File

@@ -29,7 +29,6 @@ and (min-width: 0px) //TODO
} }
a { a {
transition: color 0.25s;
color: #AB0000; color: #AB0000;
text-decoration: none; text-decoration: none;
&:hover { &:hover {
@@ -147,6 +146,15 @@ and (min-width: 0px) //TODO
.post, .thread { .post, .thread {
margin-bottom: 50px; margin-bottom: 50px;
.post-content, .thread-content {
margin-top: 10px;
clear: both;
word-wrap: break-word;
overflow: hidden;
img {
max-width: 100%;
}
}
.post-title, .thread-title { .post-title, .thread-title {
margin-bottom: 10px; margin-bottom: 10px;
word-wrap: break-word; word-wrap: break-word;
@@ -164,11 +172,6 @@ and (min-width: 0px) //TODO
float: right; float: right;
} }
} }
h1 {
color: inherit !important;
font-weight: bold !important;
text-shadow: none !important;
}
.post-info, .thread-info { .post-info, .thread-info {
border-bottom: 2px dashed #999; border-bottom: 2px dashed #999;
color: #888; color: #888;
@@ -181,57 +184,12 @@ and (min-width: 0px) //TODO
} }
.post-edit, .thread-edit { .post-edit, .thread-edit {
float: right; float: right;
margin-right: 10px;
}
time {
float: right;
} }
} }
.post-content, .thread-content {
margin-top: 10px;
clear: both;
word-wrap: break-word;
overflow: hidden;
img {
max-width: 100%;
}
}
pre {
background: #ddd;
padding: 0.5em;
}
code {
background: #ddd;
}
table, tr, td, th {
border-collapse: collapse;
}
pre code {
background: inherit;
padding: 0;
}
blockquote {
background: #f9f9f9;
border-left: 10px solid #ccc;
margin: 1.5em 10px;
padding: 0.5em 10px;
quotes: "\201C""\201D""\2018""\2019";
}
blockquote:before {
color: #ccc;
content: open-quote;
font-size: 4em;
line-height: 0.1em;
margin-right: 0.25em;
vertical-align: -0.4em;
}
blockquote p {
display: inline;
}
table { table {
tr, td, th { tr, td, th {
border-collapse: collapse; border-collapse: collapse;
@@ -254,48 +212,91 @@ and (min-width: 0px) //TODO
} }
} }
.role { pre {
background: #ddd;
padding: 0.5em;
overflow: auto;
}
code {
background: #ddd;
}
table, tr, td, th {
border-collapse: collapse;
}
pre code {
background: inherit;
padding: 0;
}
blockquote {
background: #f9f9f9;
border-left: 10px solid #ccc;
margin: 1.5em 10px;
padding: 0.5em 10px;
quotes: "\201C""\201D""\2018""\2019";
}
blockquote:before {
color: #ccc;
content: open-quote;
font-size: 4em;
line-height: 0.1em;
margin-right: 0.25em;
vertical-align: -0.4em;
}
blockquote p {
display: inline; display: inline;
text-decoration: none; }
border-bottom: 2px solid;
&.superadmin, &.admin {
border-color: #d22; .user {
display: inline-block;
a.role {
display: inline-block;
color: #fff;
font-weight: bold;
padding: 0 0.5em;
border-radius: 3px;
&.superadmin, &.admin {
background: #d22;
}
&.mod {
background: #6af;
}
&.normal {
background: #aaa;
}
&.banned, &.disabled {
background: #ccc;
text-decoration: line-through;
color: #888;
}
&:hover {
color: #ddd;
}
} }
&.mod { .donor {
border-color: #6af; background: #f60;
border-radius: 3px;
color: #fff;
font-weight: bold;
margin-left: 3px;
padding: 0 0.5em;
display: inline-block;
} }
&.donor { .ign {
border-color: #fa0; display: block;
} color: #000;
&.default { font-style: italic;
border-color: #000;
}
&.unconfirmed {
border-color: #888;
}
&.banned, &.disabled {
border-color: #ccc;
} }
} }
#comments { #comments {
margin: 50px 0 0 40px; margin: 50px 0 0 40px;
.comment {
margin-bottom: 12px;
padding: 15px;
border: 1px solid #555;
&:hover .editlink {
opacity: 1;
margin-right: 0;
}
&.author {
border: 3px solid #555;
}
.comment-content {
word-wrap: break-word;
overflow: hidden;
}
}
} }
#markdown-note { #markdown-note {
@@ -332,7 +333,7 @@ and (min-width: 0px) //TODO
background: #ddd; background: #ddd;
border: none; border: none;
height: 3em; height: 3em;
margin: 0 0 1px 0; margin: 0;
padding: 0.5em 1em; padding: 0.5em 1em;
width: 100%; width: 100%;
} }
@@ -374,11 +375,16 @@ and (min-width: 0px) //TODO
input, select, textarea { input, select, textarea {
border: 1px solid #f00; border: 1px solid #f00;
box-shadow: 0 0 5px #faa inset; box-shadow: 0 0 5px #faa inset;
border-bottom: none;
} }
.error { .validation-error {
color: #f60; display: inline-block;
display: inline; padding: 0 1em;
margin-left: 0.5em; width: 100%;
background: #faa;
font-weight: bold;
border: 1px solid #f00;
border-top: none;
} }
} }
@@ -459,34 +465,20 @@ and (min-width: 0px) //TODO
#userlist { #userlist {
.list-user { .list-user {
margin: 5px 0; margin: 15px 0;
display: table; a img {
a { vertical-align: middle;
color: #787878; border: 1px solid;
display: inline-block; border-radius: 3px;
color: #222;
&:hover { &:hover {
color: #AB0000; color: #AB0000;
} }
} }
a.avatar_url { .detail {
float: left; display: inline-block;
height: 64px; vertical-align: middle;
width: 64px; margin-left: 3px;
background-color: #fff;
}
.user-info {
margin-left: 10px;
float: left;
span {
display: block;
}
.user-name {
font-weight: bold;
}
.user-ign {
color: #888;
font-style: italic;
}
} }
} }
} }
@@ -508,43 +500,71 @@ and (min-width: 0px) //TODO
visibility: hidden; visibility: hidden;
} }
#forum_groups { .item-group {
.item_group { margin: 10px 0;
margin: 10px 0; &.with-avatar {
&:hover .editlink { margin-left: 70px;
opacity: 1; .avatar {
margin-right: 0; float: left;
margin-left: -70px;
border: 1px solid #ccc;
border-radius: 5px;
} }
.header { }
background: #ddd; &:hover .editlink {
border-radius: 5px 5px 0 0; opacity: 1;
margin-right: 0;
}
.header {
background: #ddd;
border-radius: 5px 5px 0 0;
padding: 0.5em;
border-bottom: 1px solid #ccc;
}
.items {
border: 1px solid #ddd;
border-top: none;
border-bottom: none;
.item {
display: block;
padding: 0.5em; padding: 0.5em;
border-bottom: 1px solid #ccc; border-bottom: 1px solid #ddd;
} &.locked:after {
.items { background-image: url('/assets/lock.png');
border: 1px solid #ddd; width: 20px;
border-top: none; height: 20px;
border-bottom: none; display: inline-block;
.item { }
display: block; &.sticky:after {
font-weight: bold; background-image: url('/assets/pin.png');
padding: 0.5em; width: 20px;
border-bottom: 1px solid #ddd; height: 20px;
&.locked:after { display: inline-block;
color: #000; }
font-weight: normal; &.sticky-locked:after {
content: " (L)"; background-image: url('/assets/lock.png');
} width: 20px;
&.sticky:before { height: 20px;
color: #000; display: inline-block;
font-weight: normal; }
content: "(S) "; &.content {
.headline {
margin: 0;
border-bottom: 1px solid #ddd;
} }
} }
} }
} }
} }
.bold {
font-weight: bold;
}
.comment-counter {
float: right;
}
.red-alert { .red-alert {
color: #AB0000; color: #AB0000;
font-weight: bold; font-weight: bold;

View File

@@ -1,6 +1,6 @@
class ApplicationController < ActionController::Base class ApplicationController < ActionController::Base
protect_from_forgery protect_from_forgery
before_filter :update_ip, :update_seen
# force_ssl # force_ssl
helper :all helper :all
@@ -10,16 +10,22 @@ class ApplicationController < ActionController::Base
helper_method :current_user helper_method :current_user
helper_method :disabled? helper_method :disabled?
helper_method :banned? helper_method :banned?
helper_method :confirmed? helper_method :normal?
helper_method :unconfirmed?
helper_method :default?
helper_method :donor?
helper_method :mod? helper_method :mod?
helper_method :admin? helper_method :admin?
helper_method :superadmin? helper_method :superadmin?
helper_method :donor?
helper_method :confirmed?
private private
def update_ip
current_user && current_user.update_attribute(:last_ip, request.remote_ip)
end
def update_seen
current_user && current_user.update_attribute(:last_seen, Time.now)
end
def current_user def current_user
@current_user ||= User.find_by_id(session[:user_id]) @current_user ||= User.find_by_id(session[:user_id])
end end
@@ -33,21 +39,8 @@ class ApplicationController < ActionController::Base
!!(current_user && current_user.banned?) !!(current_user && current_user.banned?)
end end
def unconfirmed? def normal?
!!(current_user && current_user.unconfirmed?) !!(current_user && current_user.normal?)
end
#special one
def confirmed?
!!(current_user && current_user.confirmed?)
end
def default?
!!(current_user && current_user.default?)
end
def donor?
!!(current_user && current_user.donor?)
end end
def mod? def mod?
@@ -62,4 +55,12 @@ class ApplicationController < ActionController::Base
!!(current_user && current_user.superadmin?) !!(current_user && current_user.superadmin?)
end end
def donor?
!!(current_user && current_user.donor?)
end
def confirmed?
!!(current_user && current_user.confirmed?)
end
end end

View File

@@ -34,7 +34,7 @@ class BlogpostsController < ApplicationController
if @post.save if @post.save
redirect_to @post, notice: 'Post has been created.' redirect_to @post, notice: 'Post has been created.'
else else
flash[:alert] = @post.errors.first flash[:alert] = "Error creating blogpost"
render action: "new" render action: "new"
end end
else else
@@ -47,7 +47,7 @@ class BlogpostsController < ApplicationController
@post = Blogpost.find(params[:id]) @post = Blogpost.find(params[:id])
if mod? || @comment.author.is?(current_user) if mod? || @comment.author.is?(current_user)
@post.user_editor = current_user @post.user_editor = current_user
if @post.update_attributes(params[:blogpost] ? params[:blogpost].slice(:title, :content, :user_editor) : {}) if @post.update_attributes(params[:blogpost].slice(:title, :content, :user_editor))
redirect_to @post, notice: 'Post has been updated.' redirect_to @post, notice: 'Post has been updated.'
else else
flash[:alert] = "There was a problem while updating the post" flash[:alert] = "There was a problem while updating the post"

View File

@@ -9,17 +9,33 @@ class ForumgroupsController < ApplicationController
end end
def edit def edit
if admin?
@group = Forumgroup.find(params[:id])
else
flash[:alert] = "You are not allowed to edit forum groups."
end
end end
def update def update
if admin?
@group = Forumgroup.find(params[:id])
if @group.update_attributes(params[:forumgroup])
flash[:notice] = "Forum group updated"
redirect_to @group
else
flash[:alert] = "Something went wrong"
render :edit
end
else
flash[:alert] = "You are not allowed to change forum groups"
end
end end
def new def new
if admin? if admin?
@group = Forumgroup.new @group = Forumgroup.new
else else
flash[:alert] = "You are not allowed to create forums." flash[:alert] = "You are not allowed to create forum groups."
redirect_to forums_path redirect_to forums_path
end end
end end
@@ -28,14 +44,14 @@ class ForumgroupsController < ApplicationController
if admin? if admin?
@group = Forumgroup.new(params[:forumgroup]) @group = Forumgroup.new(params[:forumgroup])
if @group.save if @group.save
flash[:notice] = "Forums created." flash[:notice] = "Forum group created."
redirect_to @group redirect_to @group
else else
flash[:alert] = "Something went wrong" flash[:alert] = "Something went wrong"
render :new render :new
end end
else else
flash[:alert] = "You are not allowed to create forums." flash[:alert] = "You are not allowed to create forum groups."
redirect_to forums_path redirect_to forums_path
end end
end end

View File

@@ -1,28 +1,19 @@
class ForumsController < ApplicationController class ForumsController < ApplicationController
before_filter :check_permission, only: [:show]
def index def index
@groups = Forumgroup.all @groups = Forumgroup.all
if current_user @groups.select!{|g| g.can_read?(current_user) }
@groups.select! do |g| @groups.sort_by!{|g| g[:position]}
g.role_read.nil? || g.role_read <= current_user.role
end
else
@groups.select!{|g| g.role_read == nil}
end
@groups.sort_by{|g| g[:position]}
end end
def show def show
@forum = Forum.find(params[:id]) @threads = @forum.forumthreads.order("sticky desc, updated_at desc")
if @forum.role_read.nil? || current_user && @forum.role_read <= current_user.role
@threads = @forum.forumthreads.order("sticky desc, updated_at desc")
else
redirect_to forums_path
end
end end
def new def new
if admin? if admin?
@group = Forumgroup.find(params[:forumgroup_id]) @group = Forumgroup.find(params[:forumgroup])
@forum = Forum.new(forumgroup: @group) @forum = Forum.new(forumgroup: @group)
else else
flash[:alert] = "You are not allowed to create a forum." flash[:alert] = "You are not allowed to create a forum."
@@ -33,7 +24,7 @@ class ForumsController < ApplicationController
def create def create
if admin? if admin?
@forum = Forum.new(params[:forum]) @forum = Forum.new(params[:forum])
@forum.forumgroup = Forumgroup.find(params[:forumgroup_id]) @forum.forumgroup = Forumgroup.find(params[:forum][:forumgroup_id])
if @forum.save if @forum.save
flash[:notice] = "Forum created." flash[:notice] = "Forum created."
redirect_to @forum redirect_to @forum
@@ -47,4 +38,16 @@ class ForumsController < ApplicationController
end end
end end
private
def check_permission
@forum = Forum.find(params[:id])
unless @forum.can_read?(current_user)
flash[:alert] = "You are not allowed to view this forum"
redirect_to forums_path
end
end
end end

View File

@@ -1,52 +1,49 @@
class ForumthreadsController < ApplicationController class ForumthreadsController < ApplicationController
before_filter :check_permission
def index def index
f = Forum.find(params[:forum_id]) redirect_to forum_path(@thread.forum.forumgroup, f)
redirect_to forum_path(f.forumgroup, f)
end end
def show def show
@thread = Forumthread.find(params[:id])
end end
def update def update
@thread = Forumthread.find(params[:id])
if mod? || @thread.author.is?(current_user) if mod? || @thread.author.is?(current_user)
@thread.user_editor = current_user @thread.user_editor = current_user
if @thread.update_attributes(params[:forumthread] ? params[:forumthread].slice(:title, :content, :user_editor) : {}) if @thread.update_attributes(params[:forumthread].slice(:title, :content, :user_editor))
redirect_to [@thread.forum, @thread], notice: 'Post has been updated.' redirect_to @thread, notice: 'Post has been updated.'
else else
flash[:alert] = "There was a problem while updating the post" flash[:alert] = "There was a problem while updating the post"
render action: "edit" render action: "edit"
end end
else else
flash[:alert] = "You are not allowed to edit this thread!" flash[:alert] = "You are not allowed to edit this thread!"
redirect_to [@thread.forum, @thread] redirect_to @thread
end end
end end
def edit def edit
@thread = Forumthread.find(params[:id])
end end
def new def new
@forum = Forum.find(params[:forum_id]) @forum = Forum.find(params[:forum_id])
if @forum && current_user && (@forum.group.role_read.nil? || @forum.group.role_read <= current_user.role) && (@forum.role_read.nil? || @forum.role_read <= current_user.role) unless @forum.can_write?(current_user)
@thread = Forumthread.new(forum: @forum) flash[:alert] = "You are not allowed to view this forum"
else redirect_to forums_path
flash[:alert] = "You are not allowed to create a new thread here!"
redirect_to @forum
end end
@thread = Forumthread.new(forum: @forum)
end end
def create def create
@forum = Forum.find(params[:forum_id]) @thread = Forumthread.new(mod? ? params[:forumthread] : params[:forumthread].slice(:title, :content))
if (confirmed? && (@forum.group.role_read || Role.get(:default))<= current_user.role && (@forum.group.role_write || Role.get(:default))<= current_user.role && (@forum.role_read || Role.get(:default))<= current_user.role && (@forum.group.role_write || Role.get(:default))<= current_user.role) if @thread.can_write?(current_user)
@thread = Forumthread.new(mod? ? params[:forumthread] : params[:forumthread].slice(:title, :content))
@thread.user_author = current_user @thread.user_author = current_user
@thread.forum = @forum @thread.forum = @thread.forum
if @thread.save if @thread.save
flash[:notice] = "Thread created!" flash[:notice] = "Thread created!"
redirect_to forum_forumthread_path(@forum, @thread) redirect_to forumthread_path( @thread)
return return
else else
flash[:alert] = "Seomthing went wrong while creating your thread." flash[:alert] = "Seomthing went wrong while creating your thread."
@@ -55,7 +52,20 @@ class ForumthreadsController < ApplicationController
end end
else else
flash[:alert] = "You are not allowed to create a thread here!" flash[:alert] = "You are not allowed to create a thread here!"
redirect_to @forum redirect_to @thread.forum
end
end
private
def check_permission
if params[:id]
@thread = Forumthread.find(params[:id])
unless @thread.can_read?(current_user)
flash[:alert] = "You are not allowed to view this thread"
redirect_to forums_path
end
end end
end end

View File

@@ -1,5 +1,4 @@
class SessionsController < ApplicationController class SessionsController < ApplicationController
require 'resolv'
def new def new
if current_user if current_user
@@ -14,22 +13,13 @@ class SessionsController < ApplicationController
unless current_user unless current_user
user = User.find_by_email(params[:email]) user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password]) if user && user.authenticate(params[:password])
hostname = ""
begin
hostname = Resolv.getname(request.remote_ip)
rescue
hostname = ""
end
user.last_ip = "#{request.remote_ip} | #{hostname}"
user.last_login = Time.now
user.save
if user.disabled? if user.disabled?
flash[:alert] = "Your account has been disabled!" flash[:alert] = "Your account has been disabled!"
elsif user.banned? elsif user.banned?
flash[:alert] = "You are banned!" flash[:alert] = "You are banned!"
else else
session[:user_id] = user.id session[:user_id] = user.id
flash[:alert] = "Remember to validate your email! Your account may be deleted soon!" if user.unconfirmed? flash[:alert] = "Remember to validate your email! Your account may be deleted soon!" if !user.confirmed?
flash[:notice] = "Logged in!" flash[:notice] = "Logged in!"
end end
else else

View File

@@ -0,0 +1,3 @@
class ThreadrepliesController < ApplicationController
end

View File

@@ -13,6 +13,7 @@ require 'open-uri'
@users = User.all @users = User.all
@users.shift() #Remove first user @users.shift() #Remove first user
end end
@users = @users.sort_by{|u| u.role}.reverse!
end end
def show def show
@@ -29,7 +30,7 @@ require 'open-uri'
flash[:notice] = "You are already signed up!" flash[:notice] = "You are already signed up!"
redirect_to current_user redirect_to current_user
else else
@user = User.new(role: Role.get(:unconfirmed)) @user = User.new
end end
end end
@@ -37,9 +38,9 @@ require 'open-uri'
if current_user if current_user
@user = User.find(params[:id]) @user = User.find(params[:id])
code = params[:code] code = params[:code]
if @user && @user.is?(current_user) && code && @user.confirm_code == code if @user && @user.is?(current_user) && code && @user.email_token == code
if @user.role == Role.get(:unconfirmed) if !confirmed?
@user.role = Role.get :default @user.confirmed = true
if @user.save if @user.save
flash[:notice] = "Registration mail confirmed." flash[:notice] = "Registration mail confirmed."
redirect_to edit_user_path(@user) redirect_to edit_user_path(@user)
@@ -49,7 +50,7 @@ require 'open-uri'
redirect_to @user redirect_to @user
return return
end end
elsif @user.role < Role.get(:unconfirmed) elsif @user.role < Role.get(:normal)
flash[:alert] = "Your account has been banned or removed" flash[:alert] = "Your account has been banned or removed"
else else
flash[:alert] = "Your account has already been confirmed!" flash[:alert] = "Your account has already been confirmed!"
@@ -60,7 +61,8 @@ require 'open-uri'
redirect_to root_path redirect_to root_path
end end
else else
flash[:alert] = "Please login" flash[:alert] = "Please login first"
cookies[:return_path] = request.fullpath
redirect_to login_path redirect_to login_path
end end
end end
@@ -78,32 +80,44 @@ require 'open-uri'
flash[:notice] = "You are already signed up!" flash[:notice] = "You are already signed up!"
redirect_to current_user redirect_to current_user
else else
@user = User.new(params[:user] ? params[:user].slice(:name, :ign, :email, :password, :password_confirmation) : {} ) @user = User.new(params[:user] ? params[:user].slice(:ign, :email, :password, :password_confirmation) : {} )
@user.role = Role.get :unconfirmed user_profile = @user.get_profile
@user.confirm_code = SecureRandom.hex(16) if user_profile
@user.last_ip = request.remote_ip @user.uuid = user_profile["id"]
@user.last_login = Time.now @user.ign = user_profile["name"] # correct case
if @user.save if validate_token(@user.uuid, @user.email, params[:registration_token])
session[:user_id] = @user.id @user.last_ip = request.remote_ip # showing in mail
if uses_mc_password?(@user.ign, params[:user][:password]) if @user.save
minecraftpw = true session[:user_id] = @user.id
flash[:alert] = "Really? That's your Minecraft password!" if @user.uses_mc_password?(params[:user][:password])
is_idiot = true
flash[:alert] = "Really? That's your Minecraft password!"
end
begin
RedstonerMailer.register_mail(@user, is_idiot).deliver
RedstonerMailer.register_info_mail(@user, is_idiot).deliver
rescue => e
puts "---"
puts "WARNING: registration mail failed for user #{@user.name}, #{@user.email}"
puts e.message
puts "---"
flash[:alert] = "Registration mail failed. Please contact us in-game."
end
flash[:notice] = "Successfully signed up! Check your email!"
redirect_to edit_user_path(@user)
else
flash[:alert] = "Something went wrong"
render action: "new"
end
@user.email_token = SecureRandom.hex(16)
else
flash[:alert] = "Token invalid for this username"
render action: "new"
end end
begin
RedstonerMailer.register_mail(@user, minecraftpw).deliver
RedstonerMailer.register_info_mail(@user, minecraftpw).deliver
rescue => e
puts "---"
puts "WARNING: registration mail failed for user #{@user.name}, #{@user.email}"
puts e.message
puts "---"
flash[:alert] = "Registration mail failed. Please contact us in-game."
end
flash[:notice] = "Successfully signed up! Check your email!"
redirect_to edit_user_path(@user)
else else
flash[:alert] = "Something went wrong" flash[:alert] = "Error. Your username is not correct or Mojang's servers are down."
render action: "new" render action: new
return
end end
end end
end end
@@ -129,10 +143,10 @@ require 'open-uri'
youtube = get_youtube(userdata[:youtube]) youtube = get_youtube(userdata[:youtube])
userdata[:youtube] = youtube[:channel] userdata[:youtube] = youtube[:channel]
userdata[:youtube_channelname] = youtube[:channel_name] userdata[:youtube_channelname] = youtube[:channel_name]
flash[:alert] = "Couldn't find a YouTube channel by that name, are you sure it's correct?" unless youtube[:is_correct?] flash[:alert] = "Couldn't find a YouTube channel with that name, are you sure it's correct?" unless youtube[:is_correct?]
end end
if @user.update_attributes(userdata) if @user.update_attributes(userdata)
flash[:notice] = 'Profile updated.' flash[:notice] = 'Profile updated.'
else else
flash[:alert] = "There was a problem while updating the profile" flash[:alert] = "There was a problem while updating the profile"
render action: "edit" render action: "edit"
@@ -158,7 +172,7 @@ require 'open-uri'
def unban def unban
@user = User.find(params[:id]) @user = User.find(params[:id])
if mod? && current_user.role >= @user.role if mod? && current_user.role >= @user.role
@user.role = Role.get :default @user.role = Role.get :normal
flash[:notice] = "\"#{@user.name}\" has been unbanned!" flash[:notice] = "\"#{@user.name}\" has been unbanned!"
else else
flash[:alert] = "You are not allowed to unban this user!" flash[:alert] = "You are not allowed to unban this user!"
@@ -212,4 +226,13 @@ require 'open-uri'
redirect_to old_user redirect_to old_user
end end
private
def validate_token(uuid, email, token)
user_token = RegisterToken.where(uuid: uuid, email: email).first
user_token && user_token.token == token
end
end end

View File

@@ -1,54 +1,6 @@
module UsersHelper module UsersHelper
require "open-uri" require "open-uri"
def avatar_url(user_id, size)
u = User.find_by_id(user_id)
u.nil? ? ign = :char : ign = u.ign
return "https://minotar.net/helm/#{CGI.escape(ign)}/#{CGI.escape(size.to_s)}"
end
def uses_mc_password?(ign, password)
query = {
user: ign,
password: password,
version: 9999 #just something high so it won't fail with "Old version"
}.to_query
begin
#check if this user is an idiot and uses their mc password.
mclogin = open("https://login.minecraft.net/?#{query}", :read_timeout => 1).read
rescue
puts "---"
puts "ERROR: failed to check mc password for '#{ign}'. Login servers down?"
puts "---"
end
!!mclogin.downcase.include?(ign.downcase)
end
def haspaid?(ign)
query = {user: ign}.to_query
begin
response = open("https://minecraft.net/haspaid.jsp?#{query}", :read_timeout => 1).read
rescue
puts "---"
puts "ERROR: failed to check for premium account for '#{ign}'. Minecraft servers down?"
puts "---"
response = "true"
end
!(response.casecmp("false") == 0)
end
def correct_case?(ign)
begin
http = Net::HTTP.start("skins.minecraft.net")
skin = http.get("/MinecraftSkins/#{CGI.escape(ign)}.png")
http.finish
rescue
puts "---"
puts "ERROR: failed to get skin status code for '#{ign}'. Skin servers down?"
puts "---"
end
skin.code != "404"
end
def get_youtube(yt_name) def get_youtube(yt_name)
yt = {channel: yt_name} yt = {channel: yt_name}
@@ -67,10 +19,4 @@ require "open-uri"
yt yt
end end
def link_user(user, html_options = {})
html_options[:class] = "role #{user.role.name} #{html_options[:class]}"
link_to(user.name, user, html_options)
end
end end

View File

@@ -1,17 +1,17 @@
class RedstonerMailer < ActionMailer::Base class RedstonerMailer < ActionMailer::Base
default from: "info@redstoner.com" default from: "info@redstoner.com"
default reply_to: "redstonerserver@gmail.com" default reply_to: "redstonerserver+website@gmail.com"
def register_mail(user, uses_mc_pass) def register_mail(user, uses_mc_pass)
@user = user @user = user
@mcpw = uses_mc_pass @mcpw = uses_mc_pass
mail(to: @user.email, subject: "Registration on Redstoner.com", from: "info@redstoner.com", reply_to: "redstonerserver@gmail.com") mail(to: @user.email, subject: "Registration on Redstoner.com", from: "info@redstoner.com", reply_to: "redstonerserver+website@gmail.com")
end end
def register_info_mail(user, uses_mc_pass) def register_info_mail(user, uses_mc_pass)
@user = user @user = user
@mcpw = uses_mc_pass @mcpw = uses_mc_pass
mail(to: "redstonerserver@gmail.com", subject: "#{@user.name} registered on Redstoner.com", from: "info@redstoner.com", reply_to: "redstonerserver@gmail.com") mail(to: "redstonerserver@gmail.com", subject: "#{@user.name} registered on Redstoner.com", from: "info@redstoner.com", reply_to: "redstonerserver+website@gmail.com")
end end
end end

View File

@@ -13,4 +13,12 @@ class Forum < ActiveRecord::Base
def group def group
forumgroup forumgroup
end end
end
def can_read?(user)
group.can_read?(user) && (role_read.nil? || (!user.nil? && user.role >= role_read))
end
def can_write?(user)
group.can_read?(user) && can_read?(user) && (role_write.nil? || (!user.nil? && user.role >= role_write))
end
end

View File

@@ -12,4 +12,12 @@ class Forumgroup < ActiveRecord::Base
def to_s def to_s
name name
end end
def can_read?(user)
role_read.nil? || (!user.nil? && user.role >= role_read)
end
def can_write?(user)
!user.nil? && can_read?(user) && user.confirmed? && (role_write.nil? || user.role >= role_write)
end
end end

View File

@@ -2,11 +2,15 @@ class Forumthread < ActiveRecord::Base
belongs_to :forum belongs_to :forum
belongs_to :user_author, class_name: "User", foreign_key: "user_author_id" belongs_to :user_author, class_name: "User", foreign_key: "user_author_id"
belongs_to :user_editor, class_name: "User", foreign_key: "user_editor_id" belongs_to :user_editor, class_name: "User", foreign_key: "user_editor_id"
has_many :threadreplies
attr_accessible :title, :content, :sticky, :locked, :user_author, :user_editor, :forum attr_accessible :title, :content, :sticky, :locked, :user_author, :user_editor, :forum
validates_presence_of :title validates_presence_of :title, :author, :forum
validates_presence_of :content validates_presence_of :content
validates_length_of :content, in: 5..10000
accepts_nested_attributes_for :threadreplies
def to_s def to_s
title title
@@ -23,4 +27,16 @@ class Forumthread < ActiveRecord::Base
def editor def editor
@editor ||= user_editor @editor ||= user_editor
end end
def replies
threadreplies
end
def can_read?(user)
forum.can_read?(user)
end
def can_write?(user)
forum.can_write?(user) && (!locked? || mod?)
end
end end

View File

@@ -0,0 +1,2 @@
class RegisterToken < ActiveRecord::Base
end

View File

@@ -25,20 +25,24 @@ class Role < ActiveRecord::Base
elsif role.is_a?(Symbol) elsif role.is_a?(Symbol)
self <=> Role.find_by_name(role) self <=> Role.find_by_name(role)
else else
raise "Cannot compare Role with #{role.class}" self.to_i <=> role
end end
end end
def self.all_until (role) def self.all_to (role)
Role.all.select do |r| Role.order(:value).select do |r|
r <= role r <= role
end end
end end
def self.all_from(role) def self.all_from(role)
Role.all.select do |r| Role.order(:value).select do |r|
r >= role r >= role
end end
end end
def self.all_from_to(from, to)
Role.order(:value).select {|r| r >= from}.select {|r| r <= to}
end
end end

22
app/models/threadreply.rb Normal file
View File

@@ -0,0 +1,22 @@
class Threadreply < ActiveRecord::Base
belongs_to :forumthread
belongs_to :user_author, class_name: "User", foreign_key: "user_author_id"
belongs_to :user_editor, class_name: "User", foreign_key: "user_editor_id"
attr_accessible :title, :content, :sticky, :locked, :user_author, :user_editor, :forumthread
validates_presence_of :content
validates_length_of :content, in: 2..10000
def author
@author ||= if self.user_author.present?
user_author
else
User.first
end
end
def editor
@editor ||= user_editor
end
end

View File

@@ -1,34 +1,43 @@
class User < ActiveRecord::Base class User < ActiveRecord::Base
include UsersHelper include UsersHelper
include Rails.application.routes.url_helpers
belongs_to :role belongs_to :role
attr_accessible :name, :password, :password_confirmation, :ign, :email, :confirm_code, :about, :last_ip, :skype, :skype_public, :youtube, :youtube_channelname, :twitter, :last_login, :role, :role_id attr_accessible :uuid, :confirmed, :name, :password, :password_confirmation, :ign, :email, :email_token, :about, :last_ip, :skype, :skype_public, :youtube, :youtube_channelname, :twitter, :last_seen, :role, :role_id
has_secure_password has_secure_password
before_validation :strip_whitespaces before_validation :strip_whitespaces, :set_uuid, :set_name, :set_role, :set_email_token
validates_presence_of :password, :password_confirmation, :confirm_code, :on => :create validates_presence_of :password, :password_confirmation, :email_token, :on => :create
validates_presence_of :name, :email, :ign validates_presence_of :name, :email, :ign
validates_length_of :password, in: 8..256, :on => :create validates_length_of :password, in: 8..256, :on => :create
validates_length_of :name, in: 3..20 validates_length_of :name, in: 2..30
validates_length_of :about, maximum: 5000 validates_length_of :about, maximum: 5000
validates_length_of :ign, minimum: 2, maximum: 100 validates_length_of :ign, minimum: 2, maximum: 16
validates :email, uniqueness: {case_sensitive: false}, format: {with: /^\S+@\S+\.[a-z]{2,}$/i, message: "That doesn't look like an email adress."} validates :email, uniqueness: {case_sensitive: false}, format: {with: /^.+@.+\..{2,}$/i, message: "That doesn't look like an email adress."}
validates :name, uniqueness: {case_sensitive: false}, format: {with: /^[a-z\d\-_ ]+$/i, message: "Allowed characters: a-z0-9, dashes, underscores and spaces"}
validates :ign, uniqueness: {case_sensitive: false}, format: {with: /^[a-z\d_]+$/i, message: "That is probably not your username."} validates :ign, uniqueness: {case_sensitive: false}, format: {with: /^[a-z\d_]+$/i, message: "That is probably not your username."}
validate :ign_is_not_skull, :ign_has_paid, :ign_has_correct_case validate :ign_has_paid
validate :ign_is_not_mojang, on: :create
has_many :blogposts has_many :blogposts
has_many :comments has_many :comments
# foo.bar.is?(current_user)
def is? (user) def is? (user)
self == user self == user
end end
def donor?
!!self.donor
end
def confirmed?
!!self.confirmed
end
#roles #roles
def disabled? def disabled?
!!(self.role == :disabled) !!(self.role == :disabled)
@@ -38,20 +47,8 @@ class User < ActiveRecord::Base
!!(self.role == :banned) !!(self.role == :banned)
end end
def unconfirmed? def normal?
!!(self.role == :unconfirmed) !!(self.role >= :normal)
end
def confirmed?
!!(self.role > :unconfirmed)
end
def default?
!!(self.role >= :default)
end
def donor?
!!(self.role >= :donor)
end end
def mod? def mod?
@@ -66,26 +63,124 @@ class User < ActiveRecord::Base
!!(self.role >= :superadmin) !!(self.role >= :superadmin)
end end
private
def ign_is_not_skull def avatar_url(size)
errors.add(:ign, "Good one...") if ["MHF_Blaze", "MHF_CaveSpider", "MHF_Chicken", "MHF_Cow", "MHF_Enderman", "MHF_Ghast", "MHF_Golem", "MHF_Herobrine", "MHF_LavaSlime", "MHF_MushroomCow", "MHF_Ocelot", "MHF_Pig", "MHF_PigZombie", "MHF_Sheep", "MHF_Slime", "MHF_Spider", "MHF_Squid", "MHF_Villager", "MHF_Cactus", "MHF_Cake", "MHF_Chest", "MHF_Melon", "MHF_OakLog", "MHF_Pumpkin", "MHF_TNT", "MHF_TNT2", "MHF_ArrowUp", "MHF_ArrowDown", "MHF_ArrowLeft", "MHF_ArrowRight", "MHF_Exclamation", "MHF_Question"].include?(self.ign) return "https://minotar.net/helm/#{CGI.escape(self.ign)}/#{CGI.escape(size.to_s)}"
end end
def ign_is_not_mojang
if self.ign.start_with?("mojang_secret_ign_")
self.ign = self.ign[18..-1] #check if this user is an idiot and uses their mc password.
else def uses_mc_password?(password)
errors.add(:ign, "If that's really you, contact <a href='/users?role=staff'>us</a> in-game.") if ["mollstam", "carlmanneh", "MinecraftChick", "Notch", "jeb_", "xlson", "jonkagstrom", "KrisJelbring", "marc", "Marc_IRL", "MidnightEnforcer", "YoloSwag4Lyfe", "EvilSeph", "Grumm", "Dinnerbone", "geuder", "eldrone", "JahKob", "BomBoy", "MansOlson", "pgeuder", "91maan90", "vubui", "PoiPoiChen", "mamirm", "eldrone", "_tomcc"].include?(self.ign) uri = URI.parse("https://authserver.mojang.com/authenticate")
http = Net::HTTP.new(uri.host, uri.port)
http.open_timeout = 5
http.read_timeout = 20
http.use_ssl = true
payload = { agent: { name: "Minecraft", version: 1 }, username: self.email, password: password }
begin
response = http.post(uri.request_uri, payload.to_json, "Content-Type" => "application/json").code
if response.code == "200"
return true
else
payload[:username] = self.ign
return http.post(uri.request_uri, payload.to_json, "Content-Type" => "application/json").code == "200"
end
rescue => e
puts "---"
puts "ERROR: failed to check mc password for '#{self.uuid}'. Login servers down?"
puts e.message
puts "---"
return false
end end
end end
def ign_has_paid def haspaid?
errors.add(:ign, "'#{self.ign}' is not a valid account!") unless haspaid?(self.ign) begin
response = open("https://sessionserver.mojang.com/session/minecraft/profile/#{CGI.escape(self.uuid)}", read_timeout: 0.5)
if response.status[0] == "200"
session_profile = JSON.load(response.read)
if session_profile["legacy"] == true
return open("https://minecraft.net/haspaid.jsp?#{{user: self.ign}.to_query}", read_timeout: 0.5).read == "true"
else
return true
end
elsif response.status[0] == "204"
return false
else
puts "---"
puts "ERROR: unexpected response code while checking '#{self.uuid}' for premium account"
puts "code: #{reponse.status}, body: '#{reponse.read}'"
puts "---"
end
rescue => e
puts "---"
puts "ERROR: failed to check for premium account for '#{self.uuid}'. Minecraft servers down?"
puts e.message
puts "---"
end
# mojang servers have trouble
return true
end end
def ign_has_correct_case # def correct_case?(ign)
errors.add(:ign, "The IGN is case-sensitive. Please correct '#{self.ign}'.") unless correct_case?(self.ign) # begin
# http = Net::HTTP.start("skins.minecraft.net")
# skin = http.get("/MinecraftSkins/#{CGI.escape(ign)}.png")
# http.finish
# rescue
# puts "---"
# puts "ERROR: failed to get skin status code for '#{ign}'. Skin servers down?"
# puts "---"
# end
# skin.code != "404"
# end
def get_profile
uri = URI.parse("https://api.mojang.com/profiles")
http = Net::HTTP.new(uri.host, uri.port)
http.open_timeout = 5
http.read_timeout = 20
http.use_ssl = true
payload = { agent: "Minecraft", name: self.ign }
begin
response = http.post(uri.request_uri, payload.to_json, "Content-Type" => "application/json")
if response.code == "200"
return JSON.load(response.body)["profiles"][0]
end
rescue => e
puts "----"
puts "Failed to get mojang profile for #{self.ign}"
puts e.message
puts "----"
return nil
end
end
private
def set_uuid
if !self.uuid.present?
# idk
end
end
def set_name
self.name ||= self.ign
end
def set_role
self.role ||= Role.get(:normal)
end
def ign_has_paid
errors.add(:ign, "'#{self.ign}' is not a valid account!") unless self.haspaid?
end end
def strip_whitespaces def strip_whitespaces
@@ -97,4 +192,20 @@ class User < ActiveRecord::Base
self.youtube.strip! if self.youtube self.youtube.strip! if self.youtube
self.twitter.strip! if self.twitter self.twitter.strip! if self.twitter
end end
def set_email_token
self.email_token = SecureRandom.hex(16)
end
# def ign_is_not_skull
# errors.add(:ign, "Good one...") if ["MHF_Blaze", "MHF_CaveSpider", "MHF_Chicken", "MHF_Cow", "MHF_Enderman", "MHF_Ghast", "MHF_Golem", "MHF_Herobrine", "MHF_LavaSlime", "MHF_MushroomCow", "MHF_Ocelot", "MHF_Pig", "MHF_PigZombie", "MHF_Sheep", "MHF_Slime", "MHF_Spider", "MHF_Squid", "MHF_Villager", "MHF_Cactus", "MHF_Cake", "MHF_Chest", "MHF_Melon", "MHF_OakLog", "MHF_Pumpkin", "MHF_TNT", "MHF_TNT2", "MHF_ArrowUp", "MHF_ArrowDown", "MHF_ArrowLeft", "MHF_ArrowRight", "MHF_Exclamation", "MHF_Question"].include?(self.ign)
# end
# def ign_is_not_mojang
# if self.ign.start_with?("mojang_secret_ign_")
# self.ign = self.ign[18..-1]
# else
# errors.add(:ign, "If that's really you, contact <a href='/users?role=staff'>us</a> in-game.") if ["mollstam", "carlmanneh", "MinecraftChick", "Notch", "jeb_", "xlson", "jonkagstrom", "KrisJelbring", "marc", "Marc_IRL", "MidnightEnforcer", "YoloSwag4Lyfe", "EvilSeph", "Grumm", "Dinnerbone", "geuder", "eldrone", "JahKob", "BomBoy", "MansOlson", "pgeuder", "91maan90", "vubui", "PoiPoiChen", "mamirm", "eldrone", "_tomcc"].include?(self.ign)
# end
# end
end end

View File

@@ -1,19 +1,20 @@
<%= link_to 'Make new Post', new_blogpost_path, class: "btn blue" if mod? %> <%= link_to 'Make new Post', new_blogpost_path, class: "btn blue" if mod? %>
<div id="posts"> <div id="posts">
<% @posts.each do |p| %> <% @posts.each do |p| %>
<div class="post" id="post-<%= p.id %>"> <div class="item-group with-avatar" id="post-<%= p.id %>">
<div class="post-title"> <%= link_to(image_tag(p.author.avatar_url(64), class: "avatar"), p.author, title: p.author.ign) %>
<h2><%= link_to truncate(p.title, length: 60, omission: " …"), p %></h2> <div class="header">
<span class="comment-counter"> <%= render partial: "users/username", locals: { user: p.author } %> <time><%= link_to p.created_at.strftime("%e. %b %Y, %H:%m"), p %></time>
<%= link_to pluralize(p.comments.count, "Comment"), p %> <span class="comment-counter">
</span> <%= link_to pluralize(p.comments.count, "Comment"), p %>
</span>
</div>
<div class="items">
<div class="item content">
<h2 class="headline"><%= link_to truncate(p.title, length: 60, omission: " …"), p %></h2>
<%= Sanitize.clean(GitHub::Markdown.render_gfm(p.content), Sanitize::Config::RELAXED).html_safe %>
</div>
</div>
</div> </div>
<div class="post-info">
by <%= link_user p.author %> on <%= p.created_at.strftime("%e. %b %Y") %>
</div>
<div class="post-content">
<%= Sanitize.clean(GitHub::Markdown.render_gfm(p.content), Sanitize::Config::RELAXED).html_safe %>
</div>
</div>
<% end %> <% end %>
</div> </div>

View File

@@ -1,20 +1,20 @@
<div class="post"> <div class="item-group post with-avatar" id="post-<%= @post.id %>">
<div class="post-title"> <%= link_to(image_tag(@post.author.avatar_url(64), class: "avatar"), @post.author, title: @post.author.ign) %>
<h1><%= @post.title %></h1> <div class="header">
<%= render partial: "users/username", locals: { user: @post.author } %> <time><%= link_to @post.created_at.strftime("%e. %b %Y, %H:%m"), p %></time>
<%= link_to "edit", edit_blogpost_path(@post.id), class: "editlink" if mod? %>
</div> </div>
<div class="post-info"><%= link_user @post.author %> on <%= @post.created_at.strftime("%e. %b %Y") %> <div class="items">
<% if mod? %> <div class="item content">
<%= link_to "edit", edit_blogpost_path(@post.id), class: "post-edit" %> <h2 class="headline"><%= link_to truncate(@post.title, length: 60, omission: " …"), p %></h2>
<% end %> <%= Sanitize.clean(GitHub::Markdown.render_gfm(@post.content), Sanitize::Config::RELAXED).html_safe %>
</div> </div>
<div class="post-content">
<%= Sanitize.clean(GitHub::Markdown.render_gfm(@post.content), Sanitize::Config::RELAXED).html_safe %>
</div>
<div id="comments">
<h3><%= "#{pluralize(@post.comments.length, 'comment')}." %></h3>
<% @post.comments.each do |c| %>
<%= render "comments/comment", :c => c %>
<% end %>
<%= render "comments/new" %>
</div> </div>
</div>
<div id="comments">
<h3><%= "#{pluralize(@post.comments.length, 'comment')}." %></h3>
<% @post.comments.each do |c| %>
<%= render "comments/comment", :c => c %>
<% end %>
<%= render "comments/new" %>
</div> </div>

View File

@@ -1,8 +1,12 @@
<div class="comment <%= "author" if c.author == @post.author %>" id="comment-<%= c.id %>"> <div class="item-group with-avatar comment" id="comment-<%= c.id %>">
<span class="comment-info"><%= link_user c.author %> <%= c.created_at.strftime("%e. %b %Y, %H:%m") %> <%= link_to(image_tag(c.author.avatar_url(64), class: "avatar"), c.author, title: c.author.ign) %>
<% if mod? || c.author.is?(current_user) %> <div class="header <%= "op" if c.author.is?(c.blogpost.author) %>">
<div class="editlink"><%= link_to "edit", edit_blogpost_comment_path(c.blogpost, c) %></div> <%= render partial: "users/username", locals: { user: c.author } %> <time><%= link_to c.created_at.strftime("%e. %b %Y, %H:%m"), [c.blogpost, c] %></time>
<% end %> <%= link_to "edit", edit_blogpost_comment_path(c.blogpost, c), class: "editlink" if (mod? || c.author.is?(current_user)) %>
</span> </div>
<div class="comment-content"><%= h(c.content).gsub(/(\s*?[\r\n]){3,}/, "\n\n").gsub("\n", "<br>").html_safe %></div> <div class="items">
<div class="item content">
<%= h(c.content).gsub(/(\s*?[\r\n]){3,}/, "\n\n").gsub("\n", "<br>").html_safe %>
</div>
</div>
</div> </div>

View File

@@ -1 +1,24 @@
<h1>Not yet</h1> <h1>Edit forum group</h1>
<% role_selection = Role.all_from_to(:normal, :admin).collect{|p|[p.name, p.id]} %>
<%= form_for @group do |f|%>
<table>
<tr>
<td><%= f.label :name %></td>
<td><%= f.text_field :name, placeholder: "Name" %></td>
</tr>
<tr>
<td><%= f.label :position %></td>
<td><%= f.number_field :position, placeholder: "Position" %></td>
</tr>
<tr>
<td><%= f.label :role_read_id, "Min. read role" %></td>
<td><%= f.select :role_read_id, role_selection, include_blank: "None" %></td>
</tr>
<tr>
<td><%= f.label :role_write_id, "Min. write role" %></td>
<td><%= f.select :role_write_id, role_selection, include_blank: false %></td>
</tr>
</table>
<%= f.submit "Update group", class: "btn blue" %>
<% end %>
<%= button_to "Delete group", @post, :method => "delete", :confirm => "Delete group?\nForums + Threads will not be accessible!", class: "btn red right" %>

View File

@@ -1,16 +1,23 @@
<h1>New forum group</h1> <h1>New forum group</h1>
<%= simple_form_for @group do |f|%> <% role_selection = Role.all_from_to(:normal, :admin).collect{|p|[p.name, p.id]} %>
<div id="form_labels"> <%= form_for @group do |f|%>
<%= f.label :name %> <table>
<%= f.label :position %> <tr>
<%= f.label :role_read_id, "Min read role" %> <td><%= f.label :name %></td>
<%= f.label :role_write_id, "Min write role" %> <td><%= f.text_field :name, placeholder: "Name" %></td>
</div> </tr>
<div id="form_inputs"> <tr>
<%= f.input :name, placeholder: "Name" %> <td><%= f.label :position %></td>
<%= f.input :position, placeholder: "Position" %> <td><%= f.number_field :position, placeholder: "Position" %></td>
<%= f.input :role_read_id, as: :select, collection: Role.all_from(Role.get :default), include_blank: "None" %> </tr>
<%= f.input :role_write_id, as: :select, collection: Role.all_from(Role.get :default), include_blank: false %> <tr>
</div> <td><%= f.label :role_read_id, "Min. read role" %></td>
<%= f.submit "Create group", class: "btn blue" %> <td><%= f.select :role_read_id, role_selection, include_blank: "None" %></td>
</tr>
<tr>
<td><%= f.label :role_write_id, "Min. write role" %></td>
<td><%= f.select :role_write_id, role_selection, include_blank: false %></td>
</tr>
</table>
<%= f.submit "Create group", class: "btn blue" %>
<% end %> <% end %>

View File

@@ -1,12 +1,13 @@
<div id="forum_groups"> <div id="forum_groups">
<% @groups.each do |group| %> <% @groups.each do |group| %>
<div class="item_group" id="forums-<%= group.id %>"> <div class="item-group" id="forums-<%= group.id %>">
<div class="header"> <div class="header">
<%= group.name %> <%= group.name %>
<%= link_to "edit", edit_forumgroup_path(group), class: "editlink" if admin? %> <%= link_to "edit", edit_forumgroup_path(group), class: "editlink" if admin? %>
<%= link_to "+", new_forum_path(forumgroup: group), class: "editlink" if admin? %>
</div> </div>
<div class="items"> <div class="items bold">
<% group.forums.each do |f| %> <% group.forums.each do |f| %>
<%= link_to f.name, f, class: "item" %> <%= link_to f.name, f, class: "item" %>
<% end %> <% end %>
@@ -14,4 +15,10 @@
</div> </div>
<% end %> <% end %>
</div> </div>
<% if admin? %>
<%= link_to "New group", new_forumgroup_path, class: "btn blue" %>
<% elsif mod? %>
<%= link_to "New group", nil, class: "btn blue", disabled: true %>
<% end %>

View File

@@ -1,17 +1,25 @@
<%= link_to @group %> → New forum <%= link_to @group, forumgroup_path(@group) %> → New forum
<h1>New forum</h1> <h1>New forum forum</h1>
<%= simple_form_for [@group, @forum] do |f|%> <% role_selection = Role.all_from_to(:normal, :admin).collect{|p|[p.name, p.id]} %>
<div id="form_labels"> <%= form_for @forum do |f|%>
<%= f.label :name %> <table>
<%= f.label :position %> <tr>
<%= f.label :role_read_id, "Min read role" %> <td><%= f.label :name %></td>
<%= f.label :role_write_id, "Min write role" %> <td><%= f.text_field :name, placeholder: "Name" %></td>
</div> </tr>
<div id="form_inputs"> <tr>
<%= f.input :name, placeholder: "Name" %> <td><%= f.label :position %></td>
<%= f.input :position, placeholder: "Position" %> <td><%= f.number_field :position, placeholder: "Position" %></td>
<%= f.input :role_read_id, as: :select, collection: Role.all_from(Role.get :default), include_blank: "None" %> </tr>
<%= f.input :role_write_id, as: :select, collection: Role.all_from(Role.get :default), include_blank: false %> <tr>
</div> <td><%= f.label :role_read_id, "Min. read role" %></td>
<%= f.submit "Create forum", class: "btn blue" %> <td><%= f.select :role_read_id, role_selection, include_blank: "None" %></td>
<% end %> </tr>
<tr>
<td><%= f.label :role_write_id, "Min. write role" %></td>
<td><%= f.select :role_write_id, role_selection, include_blank: false %></td>
</tr>
</table>
<%= f.hidden_field :forumgroup_id, value: @group.id %>
<%= f.submit "Create forum", class: "btn blue" %>
<% end %>

View File

@@ -1,14 +1,20 @@
<%= link_to @forum.group, forumgroup_path(@forum.group) %> → <%= link_to @forum %> <%= link_to @forum.group, forumgroup_path(@forum.group) %> → <%= link_to @forum %>
<p><%= link_to "New thread", new_forum_forumthread_path(@forum), class: "btn blue" %></p> <h1><%= @forum %></h1>
<div id="forum_groups"> <div id="forum_groups">
<div id="forum-<%= @forum.id %>" class="item_group"> <% @threads.each do |thread| %>
<div class="header"> <div class="item-group with-avatar" id="thread-<%= thread.id %>">
<%= @forum.name %> <%= link_to "edit", edit_forum_path(@forum), class: "editlink" %> <%= link_to(image_tag(thread.author.avatar_url(64), class: "avatar"), thread.author, title: thread.author.ign) %>
<div class="header">
<%= render partial: "users/username", locals: { user: thread.author } %> <time><%= link_to thread.created_at.strftime("%e. %b %Y, %H:%m"), thread %></time>
<span class="comment-counter">
<%= link_to pluralize(thread.replies.count, "Reply"), thread %>
</span>
</div>
<div class="items bold">
<%= link_to thread.title, forumthread_path(thread), class: "item#{" locked" if thread.locked}#{" sticky" if thread.sticky}" %>
</div>
</div> </div>
<div class="items"> <% end %>
<% @threads.each do |thread| %> </div>
<%= link_to thread.title, forum_forumthread_path(@forum, thread), class: "item#{" locked" if thread.locked}#{" sticky" if thread.sticky}" %> <p><%= link_to "New thread", new_forumthread_path(forum_id: @forum), class: "btn blue" %></p>
<% end %>
</div>

View File

@@ -1,17 +1,22 @@
<%= link_to @forum.group, forumgroup_path(@forum.group) %> → <%= link_to @forum, @forum %> → New thread <%= link_to @forum.group, forumgroup_path(@forum.group) %> → <%= link_to @forum, @forum %> → New thread
<h1>New thread</h1> <h1>New thread</h1>
<%= simple_form_for [@forum, @thread] do |f|%> <%= form_for [@forum, @thread] do |f|%>
<% if mod? %> <table>
<%= f.label :sticky %> <%= f.input :sticky %> <% if mod? %>
<%= f.label :locked %> <%= f.input :locked %> <tr>
<% end %> <td><%= f.label :sticky %></td>
<div id="form_labels"> <td><%= f.check_box :sticky %></td>
<%= f.label :title %> </tr>
</div> <tr>
<td><%= f.label :locked %></td>
<td><%= f.check_box :locked %></td>
</tr>
<% end %>
</table>
<div id="form_inputs"> <div id="form_inputs">
<%= f.input :title, placeholder: "Title" %> <%= f.text_field :title, placeholder: "Title" %>
</div> </div>
<%= f.hidden_field :content, id: "epic-textarea", placeholder: "Text" %> <%= f.hidden_field :content, id: "epic-textarea", placeholder: "Text" %>
<div id="epic"></div> <div id="epic"></div>
<%= f.submit "Create thread", class: "btn blue" %><br> <p><%= f.submit "Create thread", class: "btn blue" %></p>
<% end %> <% end %>

View File

@@ -1,16 +1,29 @@
<%= link_to @thread.forum.group, forumgroup_path(@thread.forum.group) %> → <%= link_to @thread.forum, @thread.forum %> → <%= link_to @thread %> <%= link_to @thread.forum.group, forumgroup_path(@thread.forum.group) %> → <%= link_to @thread.forum, @thread.forum %> → <%= link_to @thread %>
<div class="post"> <div>
<div class="thread-title"> <%= params.inspect %><br>
<h1><%= @thread.title %></h1> <%= @forum.inspect %><br>
<%= @thread.forum.inspect %>
</div>
<div class="item-group thread with-avatar" id="thread-<%= @thread.id %>">
<%= link_to(image_tag(@thread.author.avatar_url(64), class: "avatar"), @thread.author, title: @thread.author.ign) %>
<div class="header">
<%= render partial: "users/username", locals: { user: @thread.author } %> <time><%= link_to @thread.created_at.strftime("%e. %b %Y, %H:%m"), p %></time>
<%= link_to "edit", edit_forumthread_path( @thread), class: "editlink" if mod? %>
</div> </div>
<div class="thread-info"><%= link_user @thread.author %> on <%= @thread.created_at.strftime("%e. %b %Y") %> <div class="items">
<% if mod? %> <div class="item content">
<%= link_to "edit", edit_forum_forumthread_path(@thread.forum, @thread), class: "thread-edit" %> <h2 class="headline"><%= link_to truncate(@thread.title, length: 60, omission: " …"), p %></h2>
<% end %> <%= Sanitize.clean(GitHub::Markdown.render_gfm(@thread.content), Sanitize::Config::RELAXED).html_safe %>
</div> </div>
<div class="thread-content">
<%= Sanitize.clean(GitHub::Markdown.render_gfm(@thread.content), Sanitize::Config::RELAXED).html_safe %>
</div> </div>
</div> </div>
<hr> <div id="replies">
(replies go here) <h3><%= "#{pluralize(@thread.replies.length, 'reply')}." %></h3>
<% @thread.replies.each do |c| %>
Reply<%# render "threadreplies/reply", :c => c %>
<% end %>
<% unless @thread.can_read?(current_user) %>
new
<%# render "threadreplies/new" %>
<% end %>
</div>

View File

@@ -5,10 +5,10 @@
<div id="userinfo" <%= "class=\"logged-out\"".html_safe unless current_user %>> <div id="userinfo" <%= "class=\"logged-out\"".html_safe unless current_user %>>
<% if current_user %> <% if current_user %>
<span id="userinfo-box"> <span id="userinfo-box">
<%= link_to current_user.name.truncate(14), current_user %><br/> <%= link_to current_user.name, current_user %><br/>
<%= link_to "Logout", logout_path %> <%= link_to "Logout", logout_path %>
</span> </span>
<%= link_to image_tag(avatar_url(current_user.id, 32), :class => "avatar"), current_user %> <%= link_to image_tag(current_user.avatar_url(32), :class => "avatar"), current_user %>
<% else %> <% else %>
<%= link_to "Log in", login_path(return_path: request.env['PATH_INFO']), action: "new" %> | <%= link_to "Sign up", signup_path %> <%= link_to "Log in", login_path(return_path: request.env['PATH_INFO']), action: "new" %> | <%= link_to "Sign up", signup_path %>
<% end %> <% end %>

View File

@@ -1,7 +1,7 @@
Hi <%= @user.name %>! Hi <%= @user.name %>!
<p>Thank you for registering on Redstoner.com!</p> <p>Thank you for registering on Redstoner.com!</p>
<p>To use your account, you need to <%= link_to "confirm", confirm_user_path(@user, code: @user.confirm_code, only_path: false) %> your email address. <p>To use your account, you need to <%= link_to "confirm", confirm_user_path(@user, code: @user.email_token, only_path: false) %> your email address.</p>
<% if @mcpw %> <% if @mcpw %>
<div> <div>
@@ -21,9 +21,12 @@ Hi <%= @user.name %>!
<p>Please click this link to confirm your registration: <p>Please click this link to confirm your registration:
<div style="background-color: #eeeeee; padding: 1em; margin: 0; text-align: center;" width="100%"> <div style="background-color: #eeeeee; padding: 1em; margin: 0; text-align: center;" width="100%">
<%= link_to "confirm my email", confirm_user_path(@user, code: @user.confirm_code, only_path: false), style: "text-decoration: none; color: #f2f2f2; padding: 0.5em 2em; background-color: #4096EE; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; display: inline-block; text-transform: uppercase;" %> <%= link_to "confirm my email", confirm_user_path(@user, code: @user.email_token, only_path: false), style: "text-decoration: none; color: #f2f2f2; padding: 0.5em 2em; background-color: #4096EE; border-radius: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; display: inline-block; text-transform: uppercase;" %>
</div> </div>
</p> </p>
<p>If you have any questions or problems, just ask one of our <%= link_to "Staff", users_path(role: "staff", only_path: false) %> in-game.</p> <p>If you have any questions or problems, just ask one of our <%= link_to "Staff", users_path(role: "staff", only_path: false) %> in-game.</p>
<p>Your Redstoner team</p> <p>Your Redstoner team</p>
<i style="color: #555;">If you did not sign up on redstoner.com you can safely ignore this email</i>

View File

@@ -0,0 +1,4 @@
<div class="user">
<%= link_to user.name, user_path(user.id), class: "role #{user.role.name}", title: user.ign %>
<% link_to_if "$", donate_statics_path, class: "donor", title: "Donator" if user.donor? %>
</div>

View File

@@ -7,68 +7,67 @@
<tbody> <tbody>
<tr> <tr>
<td></td> <td></td>
<td><%= image_tag avatar_url(@user.id, 128), :class => "user-avatar avatar", :alt => "avatar" %></td> <td><%= image_tag @user.avatar_url(128), :class => "user-avatar avatar", :alt => "avatar" %></td>
</tr> </tr>
<tr> <tr>
<td>Display name</td> <td>Display name</td>
<td> <td>
<%= f.input :name, :label => false, disabled: !can_edit? %> <%= f.input :name, :label => false, disabled: !can_edit? %>
</td> </td>
</tr> </tr>
<td>In-game name</td> <% if mod? %>
<td> <tr>
<%= f.input :ign, :label => false, disabled: !(mod? && current_user.role >= @user.role) %> <td>In-game name</td>
</td> <td>
</tr> <%= f.input :ign, :label => false, disabled: !(mod? && current_user.role >= @user.role) %>
<tr> </td>
<td>Role</td> </tr>
<td> <tr>
<% if mod? && current_user.role >= @user.role %> <td>Role</td>
<%= f.association :role, :label => false, :collection => Role.all_until(current_user.role), :include_blank => false %> <td>
<% else %> <% if mod? && current_user.role >= @user.role %>
<%= f.input :role, label: false, disabled: true %> <%= f.association :role, :label => false, :collection => Role.all_to(current_user.role), :include_blank => false %>
<% end %> <% else %>
</td> <%= f.input :role, label: false, disabled: true %>
</td> <% end %>
</tr> </td>
<tr> </tr>
<td> <% end %>
Skype username <tr>
</td> <td>Skype username</td>
<td> <td>
<%= f.input :skype, label: false, placeholder: "Skype username", disabled: !can_edit? %> <%= f.input :skype, label: false, placeholder: "Skype username", disabled: !can_edit? %>
</td> </td>
</tr> </tr>
<tr> <tr>
<td> <td>Show Skype to</td>
Show Skype to <td>
</td> <%= f.input :skype_public, label: false, as: :select, collection: [["Staff only", false], ["All users", true]], include_blank: false, input_html: { disabled: !can_edit? } %>
<td> </td>
<%= f.input :skype_public, label: false, as: :select, collection: [["Staff only", false], ["All users", true]], include_blank: false, input_html: { disabled: !can_edit? } %> </tr>
</td> <tr>
<tr> <td>YouTube username</td>
<td>YouTube username</td> <td>
<td> <%= f.input :youtube, :label => false, placeholder: "YouTube username", disabled: !can_edit? %>
<%= f.input :youtube, :label => false, placeholder: "YouTube username", disabled: !can_edit? %> </td>
</td> </tr>
</tr> <tr>
<tr> <td>Twitter username</td>
<td>Twitter username</td> <td>
<td> <%= f.input :twitter, :label => false, placeholder: "Twitter username", disabled: !(@user.is?(current_user) && confirmed? || (mod? && current_user.role >= @user.role)) %>
<%= f.input :twitter, :label => false, placeholder: "Twitter username", disabled: !(@user.is?(current_user) && confirmed? || (mod? && current_user.role >= @user.role)) %> </td>
</td> </tr>
</tr> <tr>
<tr> <td>About you</td>
<td>About you</td> <td>
<td> <%= f.input :about, :label => false, :input_html => {:class => "vertical"}, placeholder: "Tell us something about you...", disabled: !can_edit? %>
<%= f.input :about, :label => false, :input_html => {:class => "vertical"}, placeholder: "Tell us something about you...", disabled: !can_edit? %> </td>
</td> </tr>
</tr> </tbody>
</tbody> </table>
</table> <%= f.submit "Save profile", class: "btn blue", disabled: (!@user.confirmed? && @user.is?(current_user)) %>
<%= f.submit "Save profile", class: "btn blue", disabled: (@user.unconfirmed? && @user.is?(current_user)) %> <% if !@user.confirmed? %>
<% if @user.unconfirmed? %> <% if @user.is?(current_user) %>
<% if @user.is?(current_user) %>
<span class='red-alert'>Please confirm your email adress first!</span> <span class='red-alert'>Please confirm your email adress first!</span>
<% else %> <% else %>
<span class='red-alert'>This user has not confirmed his email!</span> <span class='red-alert'>This user has not confirmed his email!</span>

View File

@@ -1,6 +1,5 @@
<% filter = params[:role] %> <% if params[:role] %>
<% if filter %> <h1>All '<%= params[:role] %>' users</h1>
<h1>All '<%= filter %>' users</h1>
<%= link_to "show all", users_path %> <%= link_to "show all", users_path %>
<% else %> <% else %>
<h1> All users </h1> <h1> All users </h1>
@@ -8,13 +7,10 @@
<div id="userlist"> <div id="userlist">
<% @users.each do |u| %> <% @users.each do |u| %>
<div class="list-user"> <div class="list-user">
<%= link_to u, class: "avatar_url" do %> <%= link_to(image_tag(u.avatar_url(64)), u) %>
<%= image_tag(avatar_url(u.id, 64), :class => "avatar", :alt => "avatar") %> <div class="detail">
<% end %> <%= render partial: "users/username", locals: { user: u } %><br>
<div class="user-info"> <i><%= u.ign %></i>
<span class="user-name"><%= link_user u %></span>
<span class="user-ign"><%= u.ign %></span>
<span class="user-role"><%= link_to u.role, users_path(:role => u.role.name) %></span>
</div> </div>
</div> </div>
<% end %> <% end %>

View File

@@ -1,19 +1,29 @@
<h1>Sign up</h1> <h1>Sign up</h1>
<%= simple_form_for @user do |f| %> <p>To get a <i>token</i>, join the Minecraft server (redstoner.com) and use the <code>/token &lt;your-email&gt;</code> command.</p>
<div id="form_labels">
<%= f.label :name, "Display name" %> <%= form_for @user do |f| %>
<%= f.label :ign, "Minecraft name" %> <table>
<%= f.label :email, "Email" %> <tr>
<%= f.label :password, "Password" %> <td><%= f.label :ign, "Minecraft name" %></td>
<%= f.label :password_confirmation, "Confirm" %> <td><%= f.text_field :ign, placeholder: "Steve", pattern: "[a-zA-Z0-9_]{2,16}", required: true, title: "Your IGN" %></td>
</div> </tr>
<div id="form_inputs"> <tr>
<%= f.input :name, placeholder: "John" %> <td><%= f.label :email, "Email" %></td>
<%= f.input :ign, placeholder: "johndoe_1337" %> <td><%= f.email_field :email, placeholder: "steve@example.com", required: true, pattern: ".+@.+", title: "enter valid email adress", "x-moz-errormessage" => "enter valid email adress" %></td>
<%= f.input :email, placeholder: "johndoe@example.com" %> </tr>
<%= f.input :password, placeholder: "••••••" %> <tr>
<%= f.input :password_confirmation, placeholder: "••••••" %> <td><%= f.label :registration_token, "Token" %></td>
</div> <td><%= text_field_tag :registration_token, nil, placeholder: "abcdef", required: true, pattern: "[a-z]{6}", title: "6 character token", "x-moz-errormessage" => "6 character token" %></td>
</tr>
<tr>
<td><%= f.label :password, "Password" %></td>
<td><%= f.password_field :password, placeholder: "secret", required: true, pattern: ".{8,}", title: "minimum 8 characters", "x-moz-errormessage" => "minimum 8 characters" %></td>
</tr>
<tr>
<td><%= f.label :password_confirmation, "Confirm" %></td>
<td><%= f.password_field :password_confirmation, placeholder: "secret", required: true, pattern: ".{8,}", title: "minimum 8 characters", "x-moz-errormessage" => "minimum 8 characters" %></td>
</tr>
</table>
<%= f.submit "Sign up", class: "btn blue" %> <%= f.submit "Sign up", class: "btn blue" %>
<% end %> <% end %>

View File

@@ -15,7 +15,7 @@
<% if @user.banned? %> <% if @user.banned? %>
<span class="user-banned">This user is banned!</span> <span class="user-banned">This user is banned!</span>
<% end %> <% end %>
<% if @user.unconfirmed? %> <% if !@user.confirmed? %>
<% if @user.is?(current_user) %> <% if @user.is?(current_user) %>
<span class="user-unconfirmed">Please confirm your email <u><%= @user.email %></u> !</span> <span class="user-unconfirmed">Please confirm your email <u><%= @user.email %></u> !</span>
<% else %> <% else %>
@@ -25,62 +25,62 @@
<% if @user.is?(current_user) %> <% if @user.is?(current_user) %>
<span class="user-unconfirmed">Your account has been disabled.</span> <span class="user-unconfirmed">Your account has been disabled.</span>
<% else %> <% else %>
<span class="user-unconfirmed">This user is disabled or does no longer exist.</span> <span class="user-unconfirmed">This account has been disabled.</span>
<% end %> <% end %>
<% end %> <% end %>
<%= image_tag avatar_url(@user.id, 128), :class => "user-avatar avatar", :alt => "avatar" %> <%= image_tag @user.avatar_url(128), :class => "user-avatar avatar", :alt => "avatar" %>
<table> <table>
<tbody> <tbody>
<tr> <tr>
<td>IGN</td> <td>IGN</td>
<td><%= @user.ign %></td> <td><%= @user.ign %></td>
</tr> </tr>
<tr> <tr>
<td>Role</td> <td>Role</td>
<td><%= link_to @user.role, users_path(:role => @user.role.name) %></td> <td><%= link_to @user.role, users_path(:role => @user.role.name) %></td>
</tr> </tr>
<% if current_user && !@user.skype.blank? && (@user.skype_public || current_user == @user || mod?) %> <% if current_user && !@user.skype.blank? && (@user.skype_public || current_user == @user || mod?) %>
<tr> <tr>
<td>Skype</td> <td>Skype</td>
<td><%= link_to @user.skype, "skype:#{@user.skype}?chat", target: "_blank" %></a></td> <td><%= link_to @user.skype, "skype:#{@user.skype}?chat", target: "_blank" %></a></td>
</tr> </tr>
<% end %> <% end %>
<% if !@user.youtube.blank? && !@user.youtube_channelname.blank? %> <% if !@user.youtube.blank? && !@user.youtube_channelname.blank? %>
<tr> <tr>
<td>YouTube</td> <td>YouTube</td>
<td><%= link_to @user.youtube_channelname, "https://youtube.com/user/#{CGI.escape(@user.youtube)}", :target => "_blank" %></td> <td><%= link_to @user.youtube_channelname, "https://youtube.com/user/#{CGI.escape(@user.youtube)}", :target => "_blank" %></td>
</tr> </tr>
<% end %> <% end %>
<% if !@user.twitter.blank? %> <% if !@user.twitter.blank? %>
<tr> <tr>
<td>Twitter</td> <td>Twitter</td>
<td><%= link_to "@#{@user.twitter}", "https://twitter.com/#{CGI.escape(@user.twitter)}", :target => "_blank" %></td> <td><%= link_to "@#{@user.twitter}", "https://twitter.com/#{CGI.escape(@user.twitter)}", :target => "_blank" %></td>
</tr> </tr>
<% end %> <% end %>
<tr> <tr>
<td>Joined</td> <td>Joined</td>
<td><%= @user.created_at.strftime("%e. %b %Y") %></td> <td><%= @user.created_at.strftime("%e. %b %Y, %H:%m") %></td>
</tr> </tr>
<% if mod? || current_user == @user %> <% if mod? || current_user == @user %>
<% if mod? %> <% if mod? %>
<tr> <tr>
<td>Last IP</td> <td>Last IP</td>
<td><%= @user.last_ip %></td> <td><%= @user.last_ip %></td>
</tr> </tr>
<% end %> <% end %>
<tr> <tr>
<td>Email</td> <td>Email</td>
<td><%= mail_to @user.email, @user.email, :subject => "Redstoner" %></td> <td><%= mail_to @user.email, @user.email, :subject => "Redstoner" %></td>
</tr> </tr>
<tr> <tr>
<td>Last login</td> <td>Last seen</td>
<td><%= @user.last_login.strftime("%e. %b %Y, %H:%M") %></td> <td><%= @user.last_seen.strftime("%e. %b %Y, %H:%M") %></td>
</tr> </tr>
<% end %> <% end %>
</tbody> </tbody>
</table> </table>
<hr> <hr>
<%= @user.about.blank? ? "<span class=\"no-about\">nothing</span>".html_safe : @user.about %> <%= @user.about.blank? ? "<span class=\"no-about\">nothing</span>".html_safe : @user.about %>
</div> </div>

View File

@@ -0,0 +1,8 @@
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
if html_tag =~ /\<label/
html_tag
else
errors = Array(instance.error_message).join(',')
%(<span class="field_with_errors">#{html_tag}<span class="validation-error">#{errors}</span></span>).html_safe
end
end

View File

@@ -23,12 +23,9 @@ Site::Application.routes.draw do
end end
end end
resources :forums, path: 'forums' do resources :forums, path: 'forums'
resources :forumthreads, path: 'threads' resources :forumthreads, path: '/forums/threads'
collection do resources :forumgroups, path: 'forums/groups'
resources :forumgroups, path: 'groups'
end
end
match '/status' => 'status#show' match '/status' => 'status#show'

View File

@@ -1,23 +0,0 @@
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name, :unique => true, :null => false
t.string :password_digest, :null => false
t.string :ign, :unique => true, :null => false
t.string :email, :unique => true, :null => false
t.string :confirm_code, :null => false
t.text :about
t.string :last_ip
t.string :skype, :unique => true
t.boolean :skype_public, :default => false
t.string :youtube, :unique => true
t.string :youtube_channelname
t.string :twitter, :unique => true
t.datetime :last_login
t.references :role, :null => false
t.timestamps
end
end
end

View File

@@ -1,11 +1,8 @@
class CreateRoles < ActiveRecord::Migration class CreateRoles < ActiveRecord::Migration
def up def change
create_table :roles do |t| create_table :roles do |t|
t.string :name t.string :name
t.integer :value t.integer :value
end end
end end
end
def down
end
end

View File

@@ -0,0 +1,26 @@
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :uuid, unique: true, null: false
t.string :name, unique: true, null: false
t.string :password_digest, null: false
t.string :ign, unique: true, null: false
t.string :email, unique: true, null: false
t.text :about
t.string :last_ip
t.string :skype, unique: true
t.boolean :skype_public, default: false
t.string :youtube, unique: true
t.string :youtube_channelname
t.string :twitter, unique: true
t.boolean :donor, default: false
t.string :email_token
t.boolean :confirmed, default: false
t.datetime :last_seen
t.references :role, null: false
t.timestamps
end
end
end

View File

@@ -1,8 +1,8 @@
class CreateBlogposts < ActiveRecord::Migration class CreateBlogposts < ActiveRecord::Migration
def change def change
create_table :blogposts do |t| create_table :blogposts do |t|
t.string :title t.string :title
t.text :content t.text :content
t.references :user_author t.references :user_author
t.references :user_editor t.references :user_editor

View File

@@ -1,7 +1,7 @@
class CreateComments < ActiveRecord::Migration class CreateComments < ActiveRecord::Migration
def change def change
create_table :comments do |t| create_table :comments do |t|
t.text :content t.text :content
t.references :user_author t.references :user_author
t.references :user_editor t.references :user_editor

View File

@@ -1,8 +1,8 @@
class CreateForumgroups < ActiveRecord::Migration class CreateForumgroups < ActiveRecord::Migration
def change def change
create_table :forumgroups do |t| create_table :forumgroups do |t|
t.string :name t.string :name
t.integer :position t.integer :position
t.references :role_read t.references :role_read
t.references :role_write t.references :role_write

View File

@@ -1,8 +1,8 @@
class CreateForums < ActiveRecord::Migration class CreateForums < ActiveRecord::Migration
def change def change
create_table :forums do |t| create_table :forums do |t|
t.string :name t.string :name
t.integer :position t.integer :position
t.references :role_read t.references :role_read
t.references :role_write t.references :role_write

View File

@@ -1,4 +1,4 @@
class AddSessionsTable < ActiveRecord::Migration class CreateSessions < ActiveRecord::Migration
def change def change
create_table :sessions do |t| create_table :sessions do |t|
t.string :session_id, :null => false t.string :session_id, :null => false

View File

@@ -0,0 +1,10 @@
class CreateRegisterTokens < ActiveRecord::Migration
def change
create_table :register_tokens, primary_key: :uuid do |t|
t.string :uuid, limit: 32, unique: true, primary: true, null: false
t.string :token, limit: 6, null: false
t.string :email, unique: true, null: false
end
change_column :register_tokens, :uuid, :string
end
end

View File

@@ -0,0 +1,13 @@
class CreateThreadreplies < ActiveRecord::Migration
def change
create_table :threadreplies do |t|
t.text :content
t.references :user_author
t.references :user_editor
t.references :forumthread
t.timestamps
end
end
end

View File

@@ -11,7 +11,7 @@
# #
# It's strongly recommended to check this file into your version control system. # It's strongly recommended to check this file into your version control system.
ActiveRecord::Schema.define(:version => 8) do ActiveRecord::Schema.define(:version => 10) do
create_table "blogposts", :force => true do |t| create_table "blogposts", :force => true do |t|
t.string "title" t.string "title"
@@ -58,6 +58,10 @@ ActiveRecord::Schema.define(:version => 8) do
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false
end end
create_table "register_tokens", :primary_key => "uuid", :force => true do |t|
t.string "token", :limit => 6, :null => false
end
create_table "roles", :force => true do |t| create_table "roles", :force => true do |t|
t.string "name" t.string "name"
t.integer "value" t.integer "value"
@@ -73,12 +77,21 @@ ActiveRecord::Schema.define(:version => 8) do
add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id" add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at" add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"
create_table "threadreplies", :force => true do |t|
t.text "content"
t.integer "user_author_id"
t.integer "user_editor_id"
t.integer "forumthread_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "users", :force => true do |t| create_table "users", :force => true do |t|
t.string "uuid", :null => false
t.string "name", :null => false t.string "name", :null => false
t.string "password_digest", :null => false t.string "password_digest", :null => false
t.string "ign", :null => false t.string "ign", :null => false
t.string "email", :null => false t.string "email", :null => false
t.string "confirm_code", :null => false
t.text "about" t.text "about"
t.string "last_ip" t.string "last_ip"
t.string "skype" t.string "skype"
@@ -86,7 +99,10 @@ ActiveRecord::Schema.define(:version => 8) do
t.string "youtube" t.string "youtube"
t.string "youtube_channelname" t.string "youtube_channelname"
t.string "twitter" t.string "twitter"
t.datetime "last_login" t.boolean "donor", :default => false
t.string "email_token"
t.boolean "confirmed", :default => false
t.datetime "last_seen"
t.integer "role_id", :null => false t.integer "role_id", :null => false
t.datetime "created_at", :null => false t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false t.datetime "updated_at", :null => false

View File

@@ -4,9 +4,7 @@
Role.create!([ Role.create!([
{name: "disabled", value: 1}, {name: "disabled", value: 1},
{name: "banned", value: 2}, {name: "banned", value: 2},
{name: "unconfirmed", value: 5}, {name: "normal", value: 10},
{name: "default", value: 10},
{name: "donor", value: 40},
{name: "mod", value: 100}, {name: "mod", value: 100},
{name: "admin", value: 200}, {name: "admin", value: 200},
{name: "superadmin", value: 500} {name: "superadmin", value: 500}
@@ -14,29 +12,30 @@ Role.create!([
userpw = SecureRandom.hex(64) userpw = SecureRandom.hex(64)
# fallback profile for deleted users
deleted_user = User.create!( deleted_user = User.create!(
uuid: "8667ba71b85a4004af54457a9734eed7",
name: "Deleted user", name: "Deleted user",
email: "redstonerserver@gmail.com", email: "redstonerserver@gmail.com",
ign: "Mojang", ign: "Steve",
about: "Hey, apparently, I do no longer exist. This is just a placeholder profile", about: "Hey, apparently, I do no longer exist. This is just a placeholder profile",
password: userpw, password: userpw,
password_confirmation: userpw, password_confirmation: userpw,
role: Role.get(:disabled), role: Role.get(:disabled),
confirm_code: SecureRandom.hex(16),
skype: "echo123", skype: "echo123",
skype_public: true, skype_public: true,
last_ip: "0.0.0.0", last_ip: "0.0.0.0",
last_login: Time.utc(0).to_datetime confirmed: true,
) last_seen: Time.utc(0).to_datetime
deleted_user.update_attribute(:ign, "Steve") )
deleted_user.update_attribute(:ign, "Steve")
User.create!( User.create!(
name: "Redstone Sheep", uuid: "9ff3d74f716940a3aa6f262ab632d2",
ign: "redstone_sheep", ign: "redstone_sheep",
email: "theredstonesheep@gmail.com", email: "theredstonesheep@gmail.com",
about: "Hi, I am the admin :)", password: "123456789", # high seructity!
password: "123456789",
password_confirmation: "123456789", password_confirmation: "123456789",
role: Role.get(:superadmin), role: Role.get(:superadmin)
confirm_code: SecureRandom.hex(16) )
)

137
erd.dot Normal file
View File

@@ -0,0 +1,137 @@
digraph Site {
rankdir = "LR";
ranksep = "0.5";
nodesep = "0.4";
pad = "0.4,0.4";
margin = "0,0";
concentrate = "true";
labelloc = "t";
fontsize = "13";
fontname = "Arial Bold";
node[ shape = "Mrecord" , fontsize = "10" , fontname = "Arial" , margin = "0.07,0.05" , penwidth = "1.0"];
edge[ fontname = "Arial" , fontsize = "7" , dir = "both" , arrowsize = "0.9" , penwidth = "1.0" , labelangle = "32" , labeldistance = "1.8"];
label = "Site domain model\n\n";
"m_ActiveRecord::SessionStore::Session" [label = <<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="134">
<tr><td align="center" valign="bottom" width="130"><font face="Arial Bold" point-size="11">ActiveRecord::SessionStore::Session</font></td></tr>
</table>
|
<table border="0" align="left" cellspacing="2" cellpadding="0" width="134">
<tr><td align="left" width="130" port="data">data <font face="Arial Italic" color="grey60">text</font></td></tr>
<tr><td align="left" width="130" port="session_id">session_id <font face="Arial Italic" color="grey60">string</font></td></tr>
</table>
>];
m_Blogpost [label = <<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="134">
<tr><td align="center" valign="bottom" width="130"><font face="Arial Bold" point-size="11">Blogpost</font></td></tr>
</table>
|
<table border="0" align="left" cellspacing="2" cellpadding="0" width="134">
<tr><td align="left" width="130" port="content">content <font face="Arial Italic" color="grey60">text</font></td></tr>
<tr><td align="left" width="130" port="title">title <font face="Arial Italic" color="grey60">string</font></td></tr>
</table>
>];
m_Comment [label = <<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="134">
<tr><td align="center" valign="bottom" width="130"><font face="Arial Bold" point-size="11">Comment</font></td></tr>
</table>
|
<table border="0" align="left" cellspacing="2" cellpadding="0" width="134">
<tr><td align="left" width="130" port="content">content <font face="Arial Italic" color="grey60">text</font></td></tr>
<tr><td align="left" width="130" port="user_editor_id">user_editor_id <font face="Arial Italic" color="grey60">integer</font></td></tr>
</table>
>];
m_Forum [label = <<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="134">
<tr><td align="center" valign="bottom" width="130"><font face="Arial Bold" point-size="11">Forum</font></td></tr>
</table>
|
<table border="0" align="left" cellspacing="2" cellpadding="0" width="134">
<tr><td align="left" width="130" port="name">name <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="position">position <font face="Arial Italic" color="grey60">integer</font></td></tr>
</table>
>];
m_Forumgroup [label = <<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="134">
<tr><td align="center" valign="bottom" width="130"><font face="Arial Bold" point-size="11">Forumgroup</font></td></tr>
</table>
|
<table border="0" align="left" cellspacing="2" cellpadding="0" width="134">
<tr><td align="left" width="130" port="name">name <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="position">position <font face="Arial Italic" color="grey60">integer</font></td></tr>
</table>
>];
m_Forumthread [label = <<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="134">
<tr><td align="center" valign="bottom" width="130"><font face="Arial Bold" point-size="11">Forumthread</font></td></tr>
</table>
|
<table border="0" align="left" cellspacing="2" cellpadding="0" width="134">
<tr><td align="left" width="130" port="content">content <font face="Arial Italic" color="grey60">text</font></td></tr>
<tr><td align="left" width="130" port="locked">locked <font face="Arial Italic" color="grey60">boolean</font></td></tr>
<tr><td align="left" width="130" port="sticky">sticky <font face="Arial Italic" color="grey60">boolean</font></td></tr>
<tr><td align="left" width="130" port="title">title <font face="Arial Italic" color="grey60">string</font></td></tr>
</table>
>];
m_RegisterToken [label = <<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="134">
<tr><td align="center" valign="bottom" width="130"><font face="Arial Bold" point-size="11">RegisterToken</font></td></tr>
</table>
|
<table border="0" align="left" cellspacing="2" cellpadding="0" width="134">
<tr><td align="left" width="130" port="email">email <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="token">token <font face="Arial Italic" color="grey60">string (6)</font></td></tr>
</table>
>];
m_Role [label = <<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="134">
<tr><td align="center" valign="bottom" width="130"><font face="Arial Bold" point-size="11">Role</font></td></tr>
</table>
|
<table border="0" align="left" cellspacing="2" cellpadding="0" width="134">
<tr><td align="left" width="130" port="name">name <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="value">value <font face="Arial Italic" color="grey60">integer</font></td></tr>
</table>
>];
m_Threadreply [label = <<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="134">
<tr><td align="center" valign="bottom" width="130"><font face="Arial Bold" point-size="11">Threadreply</font></td></tr>
</table>
|
<table border="0" align="left" cellspacing="2" cellpadding="0" width="134">
<tr><td align="left" width="130" port="content">content <font face="Arial Italic" color="grey60">text</font></td></tr>
</table>
>];
m_User [label = <<table border="0" align="center" cellspacing="0.5" cellpadding="0" width="134">
<tr><td align="center" valign="bottom" width="130"><font face="Arial Bold" point-size="11">User</font></td></tr>
</table>
|
<table border="0" align="left" cellspacing="2" cellpadding="0" width="134">
<tr><td align="left" width="130" port="about">about <font face="Arial Italic" color="grey60">text</font></td></tr>
<tr><td align="left" width="130" port="confirmed">confirmed <font face="Arial Italic" color="grey60">boolean</font></td></tr>
<tr><td align="left" width="130" port="donor">donor <font face="Arial Italic" color="grey60">boolean</font></td></tr>
<tr><td align="left" width="130" port="email">email <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="email_token">email_token <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="ign">ign <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="last_ip">last_ip <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="last_seen">last_seen <font face="Arial Italic" color="grey60">datetime</font></td></tr>
<tr><td align="left" width="130" port="name">name <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="password_digest">password_digest <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="skype">skype <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="skype_public">skype_public <font face="Arial Italic" color="grey60">boolean</font></td></tr>
<tr><td align="left" width="130" port="twitter">twitter <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="uuid">uuid <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="youtube">youtube <font face="Arial Italic" color="grey60">string</font></td></tr>
<tr><td align="left" width="130" port="youtube_channelname">youtube_channelname <font face="Arial Italic" color="grey60">string</font></td></tr>
</table>
>];
m_User -> m_Blogpost [arrowhead = "normal", arrowtail = "none", weight = "1"];
m_User -> m_Blogpost [arrowhead = "normal", arrowtail = "none", weight = "1"];
m_Blogpost -> m_Comment [arrowhead = "normal", arrowtail = "none", weight = "2"];
m_User -> m_Comment [arrowhead = "normal", arrowtail = "none", weight = "1"];
m_Forumgroup -> m_Forum [arrowhead = "normal", arrowtail = "none", weight = "2"];
m_Role -> m_Forumgroup [arrowhead = "normal", arrowtail = "none", weight = "1"];
m_Role -> m_Forumgroup [arrowhead = "normal", arrowtail = "none", weight = "1"];
m_Forum -> m_Forumthread [arrowhead = "normal", arrowtail = "none", weight = "2"];
m_Role -> m_Forum [arrowhead = "normal", arrowtail = "none", weight = "1"];
m_Role -> m_Forum [arrowhead = "normal", arrowtail = "none", weight = "1"];
m_User -> m_Forumthread [arrowhead = "normal", arrowtail = "none", weight = "1"];
m_User -> m_Forumthread [arrowhead = "normal", arrowtail = "none", weight = "1"];
m_Forumthread -> m_Threadreply [arrowhead = "normal", arrowtail = "none", weight = "2"];
m_User -> m_Threadreply [arrowhead = "normal", arrowtail = "none", weight = "1"];
m_User -> m_Threadreply [arrowhead = "normal", arrowtail = "none", weight = "1"];
m_Role -> m_User [arrowhead = "normal", arrowtail = "none", weight = "2"];
m_User -> m_Blogpost [arrowhead = "normal", arrowtail = "none", weight = "1"];
m_User -> m_Comment [arrowhead = "normal", arrowtail = "none", weight = "1"];
}

345
erd.svg Normal file
View File

@@ -0,0 +1,345 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.26.3 (20100126.1600)
-->
<!-- Title: Site Pages: 1 -->
<svg width="1006pt" height="526pt"
viewBox="0.00 0.00 1005.60 525.60" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph1" class="graph" transform="scale(1 1) rotate(0) translate(28.8 496.8)">
<title>Site</title>
<polygon fill="white" stroke="white" points="-28.8,29.8 -28.8,-496.8 977.8,-496.8 977.8,29.8 -28.8,29.8"/>
<text text-anchor="middle" x="474" y="-452.3" font-family="Arial Bold" font-size="13.00">Site domain model</text>
<!-- m_ActiveRecord::SessionStore::Session -->
<g id="node1" class="node"><title>m_ActiveRecord::SessionStore::Session</title>
<polyline fill="none" stroke="black" points="12,-54.5 208,-54.5 "/>
<path fill="none" stroke="black" d="M208,-54.5C214,-54.5 220,-60.5 220,-66.5"/>
<polyline fill="none" stroke="black" points="220,-66.5 220,-133.5 "/>
<path fill="none" stroke="black" d="M220,-133.5C220,-139.5 214,-145.5 208,-145.5"/>
<polyline fill="none" stroke="black" points="208,-145.5 12,-145.5 "/>
<path fill="none" stroke="black" d="M12,-145.5C6,-145.5 0,-139.5 0,-133.5"/>
<polyline fill="none" stroke="black" points="0,-133.5 0,-66.5 "/>
<path fill="none" stroke="black" d="M0,-66.5C0,-60.5 6,-54.5 12,-54.5"/>
<text text-anchor="start" x="5.5" y="-132.433" font-family="Arial Bold" font-size="11.00">ActiveRecord::SessionStore::Session</text>
<polyline fill="none" stroke="black" points="0,-125.5 220,-125.5 "/>
<text text-anchor="start" x="45" y="-111.167" font-family="Arial" font-size="10.00">created_at </text>
<text text-anchor="start" x="98" y="-111.167" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
<text text-anchor="start" x="45" y="-98.6667" font-family="Arial" font-size="10.00">data </text>
<text text-anchor="start" x="70" y="-98.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">text</text>
<text text-anchor="start" x="45" y="-86.6667" font-family="Arial" font-size="10.00">id </text>
<text text-anchor="start" x="57" y="-86.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="45" y="-74.6667" font-family="Arial" font-size="10.00">session_id </text>
<text text-anchor="start" x="97" y="-74.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="45" y="-62.6667" font-family="Arial" font-size="10.00">updated_at </text>
<text text-anchor="start" x="101" y="-62.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
</g>
<!-- m_Blogpost -->
<g id="node2" class="node"><title>m_Blogpost</title>
<polyline fill="none" stroke="black" points="632.5,-73.5 753.5,-73.5 "/>
<path fill="none" stroke="black" d="M753.5,-73.5C759.5,-73.5 765.5,-79.5 765.5,-85.5"/>
<polyline fill="none" stroke="black" points="765.5,-85.5 765.5,-176.5 "/>
<path fill="none" stroke="black" d="M765.5,-176.5C765.5,-182.5 759.5,-188.5 753.5,-188.5"/>
<polyline fill="none" stroke="black" points="753.5,-188.5 632.5,-188.5 "/>
<path fill="none" stroke="black" d="M632.5,-188.5C626.5,-188.5 620.5,-182.5 620.5,-176.5"/>
<polyline fill="none" stroke="black" points="620.5,-176.5 620.5,-85.5 "/>
<path fill="none" stroke="black" d="M620.5,-85.5C620.5,-79.5 626.5,-73.5 632.5,-73.5"/>
<text text-anchor="start" x="666" y="-175.433" font-family="Arial Bold" font-size="11.00">Blogpost</text>
<polyline fill="none" stroke="black" points="620.5,-168.5 765.5,-168.5 "/>
<text text-anchor="start" x="628" y="-154.167" font-family="Arial" font-size="10.00">content </text>
<text text-anchor="start" x="665" y="-154.167" font-family="Arial Italic" font-size="10.00" fill="#999999">text</text>
<text text-anchor="start" x="628" y="-141.667" font-family="Arial" font-size="10.00">created_at </text>
<text text-anchor="start" x="681" y="-141.667" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
<text text-anchor="start" x="628" y="-129.667" font-family="Arial" font-size="10.00">id </text>
<text text-anchor="start" x="640" y="-129.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="628" y="-117.667" font-family="Arial" font-size="10.00">title </text>
<text text-anchor="start" x="648" y="-117.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="628" y="-105.667" font-family="Arial" font-size="10.00">updated_at </text>
<text text-anchor="start" x="684" y="-105.667" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
<text text-anchor="start" x="628" y="-93.6667" font-family="Arial" font-size="10.00">user_author_id </text>
<text text-anchor="start" x="698" y="-93.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="628" y="-81.6667" font-family="Arial" font-size="10.00">user_editor_id </text>
<text text-anchor="start" x="696" y="-81.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
</g>
<!-- m_Comment -->
<g id="node3" class="node"><title>m_Comment</title>
<polyline fill="none" stroke="black" points="814.5,-73.5 935.5,-73.5 "/>
<path fill="none" stroke="black" d="M935.5,-73.5C941.5,-73.5 947.5,-79.5 947.5,-85.5"/>
<polyline fill="none" stroke="black" points="947.5,-85.5 947.5,-176.5 "/>
<path fill="none" stroke="black" d="M947.5,-176.5C947.5,-182.5 941.5,-188.5 935.5,-188.5"/>
<polyline fill="none" stroke="black" points="935.5,-188.5 814.5,-188.5 "/>
<path fill="none" stroke="black" d="M814.5,-188.5C808.5,-188.5 802.5,-182.5 802.5,-176.5"/>
<polyline fill="none" stroke="black" points="802.5,-176.5 802.5,-85.5 "/>
<path fill="none" stroke="black" d="M802.5,-85.5C802.5,-79.5 808.5,-73.5 814.5,-73.5"/>
<text text-anchor="start" x="846" y="-175.433" font-family="Arial Bold" font-size="11.00">Comment</text>
<polyline fill="none" stroke="black" points="802.5,-168.5 947.5,-168.5 "/>
<text text-anchor="start" x="810" y="-154.167" font-family="Arial" font-size="10.00">blogpost_id </text>
<text text-anchor="start" x="868" y="-154.167" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="810" y="-141.667" font-family="Arial" font-size="10.00">content </text>
<text text-anchor="start" x="847" y="-141.667" font-family="Arial Italic" font-size="10.00" fill="#999999">text</text>
<text text-anchor="start" x="810" y="-129.667" font-family="Arial" font-size="10.00">created_at </text>
<text text-anchor="start" x="863" y="-129.667" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
<text text-anchor="start" x="810" y="-117.667" font-family="Arial" font-size="10.00">id </text>
<text text-anchor="start" x="822" y="-117.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="810" y="-105.667" font-family="Arial" font-size="10.00">updated_at </text>
<text text-anchor="start" x="866" y="-105.667" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
<text text-anchor="start" x="810" y="-93.6667" font-family="Arial" font-size="10.00">user_author_id </text>
<text text-anchor="start" x="880" y="-93.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="810" y="-81.6667" font-family="Arial" font-size="10.00">user_editor_id </text>
<text text-anchor="start" x="878" y="-81.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
</g>
<!-- m_Blogpost&#45;&gt;m_Comment -->
<g id="edge6" class="edge"><title>m_Blogpost&#45;&gt;m_Comment</title>
<path fill="none" stroke="black" d="M765.716,-131C774.775,-131 784.087,-131 793.272,-131"/>
<polygon fill="black" stroke="black" points="793.4,-134.15 802.4,-131 793.4,-127.85 793.4,-134.15"/>
</g>
<!-- m_Forum -->
<g id="node4" class="node"><title>m_Forum</title>
<polyline fill="none" stroke="black" points="450.5,-301.5 571.5,-301.5 "/>
<path fill="none" stroke="black" d="M571.5,-301.5C577.5,-301.5 583.5,-307.5 583.5,-313.5"/>
<polyline fill="none" stroke="black" points="583.5,-313.5 583.5,-392.5 "/>
<path fill="none" stroke="black" d="M583.5,-392.5C583.5,-398.5 577.5,-404.5 571.5,-404.5"/>
<polyline fill="none" stroke="black" points="571.5,-404.5 450.5,-404.5 "/>
<path fill="none" stroke="black" d="M450.5,-404.5C444.5,-404.5 438.5,-398.5 438.5,-392.5"/>
<polyline fill="none" stroke="black" points="438.5,-392.5 438.5,-313.5 "/>
<path fill="none" stroke="black" d="M438.5,-313.5C438.5,-307.5 444.5,-301.5 450.5,-301.5"/>
<text text-anchor="start" x="491" y="-391.433" font-family="Arial Bold" font-size="11.00">Forum</text>
<polyline fill="none" stroke="black" points="438.5,-384.5 583.5,-384.5 "/>
<text text-anchor="start" x="446" y="-370.167" font-family="Arial" font-size="10.00">forumgroup_id </text>
<text text-anchor="start" x="514" y="-370.167" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="446" y="-357.667" font-family="Arial" font-size="10.00">id </text>
<text text-anchor="start" x="458" y="-357.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="446" y="-345.667" font-family="Arial" font-size="10.00">name </text>
<text text-anchor="start" x="475" y="-345.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="446" y="-333.667" font-family="Arial" font-size="10.00">position </text>
<text text-anchor="start" x="486" y="-333.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="446" y="-321.667" font-family="Arial" font-size="10.00">role_read_id </text>
<text text-anchor="start" x="507" y="-321.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="446" y="-309.667" font-family="Arial" font-size="10.00">role_write_id </text>
<text text-anchor="start" x="507" y="-309.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
</g>
<!-- m_Forumthread -->
<g id="node6" class="node"><title>m_Forumthread</title>
<polyline fill="none" stroke="black" points="632.5,-277.5 753.5,-277.5 "/>
<path fill="none" stroke="black" d="M753.5,-277.5C759.5,-277.5 765.5,-283.5 765.5,-289.5"/>
<polyline fill="none" stroke="black" points="765.5,-289.5 765.5,-416.5 "/>
<path fill="none" stroke="black" d="M765.5,-416.5C765.5,-422.5 759.5,-428.5 753.5,-428.5"/>
<polyline fill="none" stroke="black" points="753.5,-428.5 632.5,-428.5 "/>
<path fill="none" stroke="black" d="M632.5,-428.5C626.5,-428.5 620.5,-422.5 620.5,-416.5"/>
<polyline fill="none" stroke="black" points="620.5,-416.5 620.5,-289.5 "/>
<path fill="none" stroke="black" d="M620.5,-289.5C620.5,-283.5 626.5,-277.5 632.5,-277.5"/>
<text text-anchor="start" x="655" y="-415.433" font-family="Arial Bold" font-size="11.00">Forumthread</text>
<polyline fill="none" stroke="black" points="620.5,-408.5 765.5,-408.5 "/>
<text text-anchor="start" x="628" y="-394.167" font-family="Arial" font-size="10.00">content </text>
<text text-anchor="start" x="665" y="-394.167" font-family="Arial Italic" font-size="10.00" fill="#999999">text</text>
<text text-anchor="start" x="628" y="-381.667" font-family="Arial" font-size="10.00">created_at </text>
<text text-anchor="start" x="681" y="-381.667" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
<text text-anchor="start" x="628" y="-369.667" font-family="Arial" font-size="10.00">forum_id </text>
<text text-anchor="start" x="670" y="-369.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="628" y="-357.667" font-family="Arial" font-size="10.00">id </text>
<text text-anchor="start" x="640" y="-357.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="628" y="-345.667" font-family="Arial" font-size="10.00">locked </text>
<text text-anchor="start" x="662" y="-345.667" font-family="Arial Italic" font-size="10.00" fill="#999999">boolean</text>
<text text-anchor="start" x="628" y="-333.667" font-family="Arial" font-size="10.00">sticky </text>
<text text-anchor="start" x="658" y="-333.667" font-family="Arial Italic" font-size="10.00" fill="#999999">boolean</text>
<text text-anchor="start" x="628" y="-321.667" font-family="Arial" font-size="10.00">title </text>
<text text-anchor="start" x="648" y="-321.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="628" y="-309.667" font-family="Arial" font-size="10.00">updated_at </text>
<text text-anchor="start" x="684" y="-309.667" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
<text text-anchor="start" x="628" y="-297.667" font-family="Arial" font-size="10.00">user_author_id </text>
<text text-anchor="start" x="698" y="-297.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="628" y="-285.667" font-family="Arial" font-size="10.00">user_editor_id </text>
<text text-anchor="start" x="696" y="-285.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
</g>
<!-- m_Forum&#45;&gt;m_Forumthread -->
<g id="edge16" class="edge"><title>m_Forum&#45;&gt;m_Forumthread</title>
<path fill="none" stroke="black" d="M583.716,-353C592.775,-353 602.087,-353 611.272,-353"/>
<polygon fill="black" stroke="black" points="611.4,-356.15 620.4,-353 611.4,-349.85 611.4,-356.15"/>
</g>
<!-- m_Forumgroup -->
<g id="node5" class="node"><title>m_Forumgroup</title>
<polyline fill="none" stroke="black" points="268.5,-217.5 389.5,-217.5 "/>
<path fill="none" stroke="black" d="M389.5,-217.5C395.5,-217.5 401.5,-223.5 401.5,-229.5"/>
<polyline fill="none" stroke="black" points="401.5,-229.5 401.5,-296.5 "/>
<path fill="none" stroke="black" d="M401.5,-296.5C401.5,-302.5 395.5,-308.5 389.5,-308.5"/>
<polyline fill="none" stroke="black" points="389.5,-308.5 268.5,-308.5 "/>
<path fill="none" stroke="black" d="M268.5,-308.5C262.5,-308.5 256.5,-302.5 256.5,-296.5"/>
<polyline fill="none" stroke="black" points="256.5,-296.5 256.5,-229.5 "/>
<path fill="none" stroke="black" d="M256.5,-229.5C256.5,-223.5 262.5,-217.5 268.5,-217.5"/>
<text text-anchor="start" x="293" y="-295.433" font-family="Arial Bold" font-size="11.00">Forumgroup</text>
<polyline fill="none" stroke="black" points="256.5,-288.5 401.5,-288.5 "/>
<text text-anchor="start" x="264" y="-274.167" font-family="Arial" font-size="10.00">id </text>
<text text-anchor="start" x="276" y="-274.167" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="264" y="-261.667" font-family="Arial" font-size="10.00">name </text>
<text text-anchor="start" x="293" y="-261.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="264" y="-249.667" font-family="Arial" font-size="10.00">position </text>
<text text-anchor="start" x="304" y="-249.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="264" y="-237.667" font-family="Arial" font-size="10.00">role_read_id </text>
<text text-anchor="start" x="325" y="-237.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="264" y="-225.667" font-family="Arial" font-size="10.00">role_write_id </text>
<text text-anchor="start" x="325" y="-225.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
</g>
<!-- m_Forumgroup&#45;&gt;m_Forum -->
<g id="edge10" class="edge"><title>m_Forumgroup&#45;&gt;m_Forum</title>
<path fill="none" stroke="black" d="M401.716,-298.958C411.059,-303.578 420.669,-308.331 430.132,-313.011"/>
<polygon fill="black" stroke="black" points="428.936,-315.933 438.4,-317.099 431.728,-310.286 428.936,-315.933"/>
</g>
<!-- m_Threadreply -->
<g id="node9" class="node"><title>m_Threadreply</title>
<polyline fill="none" stroke="black" points="814.5,-295.5 935.5,-295.5 "/>
<path fill="none" stroke="black" d="M935.5,-295.5C941.5,-295.5 947.5,-301.5 947.5,-307.5"/>
<polyline fill="none" stroke="black" points="947.5,-307.5 947.5,-398.5 "/>
<path fill="none" stroke="black" d="M947.5,-398.5C947.5,-404.5 941.5,-410.5 935.5,-410.5"/>
<polyline fill="none" stroke="black" points="935.5,-410.5 814.5,-410.5 "/>
<path fill="none" stroke="black" d="M814.5,-410.5C808.5,-410.5 802.5,-404.5 802.5,-398.5"/>
<polyline fill="none" stroke="black" points="802.5,-398.5 802.5,-307.5 "/>
<path fill="none" stroke="black" d="M802.5,-307.5C802.5,-301.5 808.5,-295.5 814.5,-295.5"/>
<text text-anchor="start" x="840" y="-397.433" font-family="Arial Bold" font-size="11.00">Threadreply</text>
<polyline fill="none" stroke="black" points="802.5,-390.5 947.5,-390.5 "/>
<text text-anchor="start" x="810" y="-376.167" font-family="Arial" font-size="10.00">content </text>
<text text-anchor="start" x="847" y="-376.167" font-family="Arial Italic" font-size="10.00" fill="#999999">text</text>
<text text-anchor="start" x="810" y="-363.667" font-family="Arial" font-size="10.00">created_at </text>
<text text-anchor="start" x="863" y="-363.667" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
<text text-anchor="start" x="810" y="-351.667" font-family="Arial" font-size="10.00">forumthread_id </text>
<text text-anchor="start" x="881" y="-351.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="810" y="-339.667" font-family="Arial" font-size="10.00">id </text>
<text text-anchor="start" x="822" y="-339.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="810" y="-327.667" font-family="Arial" font-size="10.00">updated_at </text>
<text text-anchor="start" x="866" y="-327.667" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
<text text-anchor="start" x="810" y="-315.667" font-family="Arial" font-size="10.00">user_author_id </text>
<text text-anchor="start" x="880" y="-315.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="810" y="-303.667" font-family="Arial" font-size="10.00">user_editor_id </text>
<text text-anchor="start" x="878" y="-303.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
</g>
<!-- m_Forumthread&#45;&gt;m_Threadreply -->
<g id="edge26" class="edge"><title>m_Forumthread&#45;&gt;m_Threadreply</title>
<path fill="none" stroke="black" d="M765.716,-353C774.775,-353 784.087,-353 793.272,-353"/>
<polygon fill="black" stroke="black" points="793.4,-356.15 802.4,-353 793.4,-349.85 793.4,-356.15"/>
</g>
<!-- m_RegisterToken -->
<g id="node7" class="node"><title>m_RegisterToken</title>
<polyline fill="none" stroke="black" points="49.5,-272.5 170.5,-272.5 "/>
<path fill="none" stroke="black" d="M170.5,-272.5C176.5,-272.5 182.5,-278.5 182.5,-284.5"/>
<polyline fill="none" stroke="black" points="182.5,-284.5 182.5,-327.5 "/>
<path fill="none" stroke="black" d="M182.5,-327.5C182.5,-333.5 176.5,-339.5 170.5,-339.5"/>
<polyline fill="none" stroke="black" points="170.5,-339.5 49.5,-339.5 "/>
<path fill="none" stroke="black" d="M49.5,-339.5C43.5,-339.5 37.5,-333.5 37.5,-327.5"/>
<polyline fill="none" stroke="black" points="37.5,-327.5 37.5,-284.5 "/>
<path fill="none" stroke="black" d="M37.5,-284.5C37.5,-278.5 43.5,-272.5 49.5,-272.5"/>
<text text-anchor="start" x="67.5" y="-326.433" font-family="Arial Bold" font-size="11.00">RegisterToken</text>
<polyline fill="none" stroke="black" points="37.5,-319.5 182.5,-319.5 "/>
<text text-anchor="start" x="45" y="-305.167" font-family="Arial" font-size="10.00">email </text>
<text text-anchor="start" x="73" y="-305.167" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="45" y="-292.667" font-family="Arial" font-size="10.00">token </text>
<text text-anchor="start" x="74" y="-292.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string (6)</text>
<text text-anchor="start" x="45" y="-280.667" font-family="Arial" font-size="10.00">uuid </text>
<text text-anchor="start" x="67" y="-280.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
</g>
<!-- m_Role -->
<g id="node8" class="node"><title>m_Role</title>
<polyline fill="none" stroke="black" points="49.5,-175.5 170.5,-175.5 "/>
<path fill="none" stroke="black" d="M170.5,-175.5C176.5,-175.5 182.5,-181.5 182.5,-187.5"/>
<polyline fill="none" stroke="black" points="182.5,-187.5 182.5,-230.5 "/>
<path fill="none" stroke="black" d="M182.5,-230.5C182.5,-236.5 176.5,-242.5 170.5,-242.5"/>
<polyline fill="none" stroke="black" points="170.5,-242.5 49.5,-242.5 "/>
<path fill="none" stroke="black" d="M49.5,-242.5C43.5,-242.5 37.5,-236.5 37.5,-230.5"/>
<polyline fill="none" stroke="black" points="37.5,-230.5 37.5,-187.5 "/>
<path fill="none" stroke="black" d="M37.5,-187.5C37.5,-181.5 43.5,-175.5 49.5,-175.5"/>
<text text-anchor="start" x="95.5" y="-229.433" font-family="Arial Bold" font-size="11.00">Role</text>
<polyline fill="none" stroke="black" points="37.5,-222.5 182.5,-222.5 "/>
<text text-anchor="start" x="45" y="-208.167" font-family="Arial" font-size="10.00">id </text>
<text text-anchor="start" x="57" y="-208.167" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="45" y="-195.667" font-family="Arial" font-size="10.00">name </text>
<text text-anchor="start" x="74" y="-195.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="45" y="-183.667" font-family="Arial" font-size="10.00">value </text>
<text text-anchor="start" x="73" y="-183.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
</g>
<!-- m_Role&#45;&gt;m_Forum -->
<g id="edge18" class="edge"><title>m_Role&#45;&gt;m_Forum</title>
<path fill="none" stroke="black" d="M182.506,-233.154C195.96,-239.798 209.168,-248.005 220,-258 244.556,-280.66 228.279,-305.345 256,-324 306.494,-357.98 375.704,-363.954 429.281,-362.135"/>
<polygon fill="black" stroke="black" points="429.558,-365.276 438.415,-361.745 429.289,-358.982 429.558,-365.276"/>
</g>
<!-- m_Role&#45;&gt;m_Forumgroup -->
<g id="edge12" class="edge"><title>m_Role&#45;&gt;m_Forumgroup</title>
<path fill="none" stroke="black" d="M182.62,-226.906C203.319,-232.01 226.01,-237.605 247.216,-242.834"/>
<polygon fill="black" stroke="black" points="246.711,-245.954 256.203,-245.05 248.219,-239.837 246.711,-245.954"/>
</g>
<!-- m_User -->
<g id="node10" class="node"><title>m_User</title>
<polyline fill="none" stroke="black" points="450,-0.5 572,-0.5 "/>
<path fill="none" stroke="black" d="M572,-0.5C578,-0.5 584,-6.5 584,-12.5"/>
<polyline fill="none" stroke="black" points="584,-12.5 584,-259.5 "/>
<path fill="none" stroke="black" d="M584,-259.5C584,-265.5 578,-271.5 572,-271.5"/>
<polyline fill="none" stroke="black" points="572,-271.5 450,-271.5 "/>
<path fill="none" stroke="black" d="M450,-271.5C444,-271.5 438,-265.5 438,-259.5"/>
<polyline fill="none" stroke="black" points="438,-259.5 438,-12.5 "/>
<path fill="none" stroke="black" d="M438,-12.5C438,-6.5 444,-0.5 450,-0.5"/>
<text text-anchor="start" x="496.5" y="-258.433" font-family="Arial Bold" font-size="11.00">User</text>
<polyline fill="none" stroke="black" points="438,-251.5 584,-251.5 "/>
<text text-anchor="start" x="445" y="-237.167" font-family="Arial" font-size="10.00">about </text>
<text text-anchor="start" x="475" y="-237.167" font-family="Arial Italic" font-size="10.00" fill="#999999">text</text>
<text text-anchor="start" x="445" y="-224.667" font-family="Arial" font-size="10.00">confirmed </text>
<text text-anchor="start" x="493" y="-224.667" font-family="Arial Italic" font-size="10.00" fill="#999999">boolean</text>
<text text-anchor="start" x="445" y="-212.667" font-family="Arial" font-size="10.00">created_at </text>
<text text-anchor="start" x="498" y="-212.667" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
<text text-anchor="start" x="445" y="-200.667" font-family="Arial" font-size="10.00">donor </text>
<text text-anchor="start" x="475" y="-200.667" font-family="Arial Italic" font-size="10.00" fill="#999999">boolean</text>
<text text-anchor="start" x="445" y="-188.667" font-family="Arial" font-size="10.00">email </text>
<text text-anchor="start" x="473" y="-188.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="445" y="-176.667" font-family="Arial" font-size="10.00">email_token </text>
<text text-anchor="start" x="504" y="-176.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="445" y="-164.667" font-family="Arial" font-size="10.00">id </text>
<text text-anchor="start" x="457" y="-164.667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="445" y="-152.667" font-family="Arial" font-size="10.00">ign </text>
<text text-anchor="start" x="462" y="-152.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="445" y="-140.667" font-family="Arial" font-size="10.00">last_ip </text>
<text text-anchor="start" x="479" y="-140.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="445" y="-128.667" font-family="Arial" font-size="10.00">last_seen </text>
<text text-anchor="start" x="493" y="-128.667" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
<text text-anchor="start" x="445" y="-116.667" font-family="Arial" font-size="10.00">name </text>
<text text-anchor="start" x="474" y="-116.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="445" y="-104.667" font-family="Arial" font-size="10.00">password_digest </text>
<text text-anchor="start" x="527" y="-104.667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="445" y="-92.6667" font-family="Arial" font-size="10.00">role_id </text>
<text text-anchor="start" x="479" y="-92.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">integer</text>
<text text-anchor="start" x="445" y="-80.6667" font-family="Arial" font-size="10.00">skype </text>
<text text-anchor="start" x="476" y="-80.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="445" y="-68.6667" font-family="Arial" font-size="10.00">skype_public </text>
<text text-anchor="start" x="509" y="-68.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">boolean</text>
<text text-anchor="start" x="445" y="-56.6667" font-family="Arial" font-size="10.00">twitter </text>
<text text-anchor="start" x="476" y="-56.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="445" y="-44.6667" font-family="Arial" font-size="10.00">updated_at </text>
<text text-anchor="start" x="501" y="-44.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">datetime</text>
<text text-anchor="start" x="445" y="-32.6667" font-family="Arial" font-size="10.00">uuid </text>
<text text-anchor="start" x="467" y="-32.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="445" y="-20.6667" font-family="Arial" font-size="10.00">youtube </text>
<text text-anchor="start" x="485" y="-20.6667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
<text text-anchor="start" x="445" y="-8.66667" font-family="Arial" font-size="10.00">youtube_channelname </text>
<text text-anchor="start" x="551" y="-8.66667" font-family="Arial Italic" font-size="10.00" fill="#999999">string</text>
</g>
<!-- m_Role&#45;&gt;m_User -->
<g id="edge32" class="edge"><title>m_Role&#45;&gt;m_User</title>
<path fill="none" stroke="black" d="M182.618,-195.78C251.987,-183.152 356.408,-164.143 428.799,-150.964"/>
<polygon fill="black" stroke="black" points="429.616,-154.017 437.907,-149.306 428.488,-147.819 429.616,-154.017"/>
</g>
<!-- m_User&#45;&gt;m_Blogpost -->
<g id="edge2" class="edge"><title>m_User&#45;&gt;m_Blogpost</title>
<path fill="none" stroke="black" d="M584.24,-133.988C593.037,-133.746 602.064,-133.498 610.976,-133.253"/>
<polygon fill="black" stroke="black" points="611.296,-136.396 620.206,-133 611.123,-130.098 611.296,-136.396"/>
</g>
<!-- m_User&#45;&gt;m_Comment -->
<g id="edge36" class="edge"><title>m_User&#45;&gt;m_Comment</title>
<path fill="none" stroke="black" d="M584.223,-74.6713C595.615,-67.8481 607.703,-61.9466 620,-58 681.785,-38.1706 703.915,-39.1306 766,-58 775.607,-60.9198 785.138,-64.9989 794.333,-69.7514"/>
<polygon fill="black" stroke="black" points="792.909,-72.5626 802.322,-74.089 795.915,-67.0261 792.909,-72.5626"/>
</g>
<!-- m_User&#45;&gt;m_Forumthread -->
<g id="edge22" class="edge"><title>m_User&#45;&gt;m_Forumthread</title>
<path fill="none" stroke="black" d="M584.24,-223.324C597.259,-238.848 610.784,-254.973 623.69,-270.362"/>
<polygon fill="black" stroke="black" points="621.376,-272.504 629.573,-277.375 626.203,-268.455 621.376,-272.504"/>
</g>
<!-- m_User&#45;&gt;m_Threadreply -->
<g id="edge28" class="edge"><title>m_User&#45;&gt;m_Threadreply</title>
<path fill="none" stroke="black" d="M584.169,-184.045C595.956,-191.083 608.174,-197.979 620,-204 682.369,-235.753 705.868,-227.19 766,-263 779.043,-270.767 792.147,-280.036 804.521,-289.627"/>
<polygon fill="black" stroke="black" points="802.699,-292.202 811.716,-295.302 806.601,-287.255 802.699,-292.202"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 30 KiB

BIN
join.mc Normal file

Binary file not shown.

BIN
ping.mc Normal file

Binary file not shown.