app/models/wiki_service.rb in instiki-0.9.2 vs app/models/wiki_service.rb in instiki-0.10.0
- old
+ new
@@ -1,83 +1,233 @@
-$:.unshift(File.dirname(__FILE__) + "/../../libraries/")
-
-require "madeleine_service"
-require "web"
-require "page"
-require "author"
-
-class WikiService < MadeleineService
- attr_reader :webs, :system
-
- def initialize
- @webs, @system = {}, {}
- end
-
- def setup?
- !@system.empty?
- end
-
- def authenticate(password)
- password == (@system["password"] || "instiki")
- end
-
- def setup(password, web_name, web_address)
- @system["password"] = password
- create_web(web_name, web_address)
- end
-
- def create_web(name, address, password = nil)
- @webs[address] = Web.new(name, address, password) unless @webs[address]
- end
-
- def update_web(old_address, new_address, name, markup, color, additional_style, safe_mode = false,
- password = nil, published = false, brackets_only = false, count_pages = false)
- if old_address != new_address
- @webs[new_address] = @webs[old_address]
- @webs.delete(old_address)
- @webs[new_address].address = new_address
- end
-
- web = @webs[new_address]
- web.refresh_revisions if settings_changed?(web, markup, safe_mode, brackets_only)
-
- web.name, web.markup, web.color, web.additional_style, web.safe_mode =
- name, markup, color, additional_style, safe_mode
-
- web.password, web.published, web.brackets_only, web.count_pages =
- password, published, brackets_only, count_pages
- end
-
- def read_page(web_address, page_name)
- web = @webs[web_address]
- web ? web.pages[page_name] : nil
- end
-
- def write_page(web_address, page_name, content, written_on, author)
- page = Page.new(@webs[web_address], page_name, content, written_on, author)
- @webs[web_address].add_page(page)
- page
- end
-
- def revise_page(web_address, page_name, content, revised_on, author)
- page = read_page(web_address, page_name)
- page.revise(content, revised_on, author)
- page
- end
-
- def rollback_page(web_address, page_name, revision_number, created_at, author_id = nil)
- page = read_page(web_address, page_name)
- page.rollback(revision_number, created_at, author_id)
- page
- end
-
- def remove_orphaned_pages(web_address)
- @webs[web_address].remove_pages(@webs[web_address].select.orphaned_pages)
- end
-
- private
- def settings_changed?(web, markup, safe_mode, brackets_only)
- web.markup != markup ||
- web.safe_mode != safe_mode ||
- web.brackets_only != brackets_only
- end
-end
\ No newline at end of file
+require 'open-uri'
+require 'yaml'
+require 'madeleine'
+require 'madeleine/automatic'
+require 'madeleine/zmarshal'
+
+require 'web'
+require 'page'
+require 'author'
+require 'file_yard'
+require 'instiki_errors'
+
+module AbstractWikiService
+
+ attr_reader :webs, :system
+
+ def authenticate(password)
+ # system['password'] variant is for compatibility with storages from older versions
+ password == (@system[:password] || @system['password'] || 'instiki')
+ end
+
+ def create_web(name, address, password = nil)
+ @webs[address] = Web.new(self, name, address, password) unless @webs[address]
+ end
+
+ def delete_web(address)
+ @webs[address] = nil
+ end
+
+ def file_yard(web)
+ raise "Web #{@web.name} does not belong to this wiki service" unless @webs.values.include?(web)
+ # TODO cache FileYards
+ FileYard.new("#{self.storage_path}/#{web.address}", web.max_upload_size)
+ end
+
+ def init_wiki_service
+ @webs = {}
+ @system = {}
+ end
+
+ def read_page(web_address, page_name)
+ ApplicationController.logger.debug "Reading page '#{page_name}' from web '#{web_address}'"
+ web = @webs[web_address]
+ if web.nil?
+ ApplicationController.logger.debug "Web '#{web_address}' not found"
+ return nil
+ else
+ page = web.pages[page_name]
+ ApplicationController.logger.debug "Page '#{page_name}' #{page.nil? ? 'not' : ''} found"
+ return page
+ end
+ end
+
+ def remove_orphaned_pages(web_address)
+ @webs[web_address].remove_pages(@webs[web_address].select.orphaned_pages)
+ end
+
+ def revise_page(web_address, page_name, content, revised_on, author)
+ page = read_page(web_address, page_name)
+ page.revise(content, revised_on, author)
+ page
+ end
+
+ def rollback_page(web_address, page_name, revision_number, created_at, author_id = nil)
+ page = read_page(web_address, page_name)
+ page.rollback(revision_number, created_at, author_id)
+ page
+ end
+
+ def setup(password, web_name, web_address)
+ @system[:password] = password
+ create_web(web_name, web_address)
+ end
+
+ def setup?
+ not (@webs.empty?)
+ end
+
+ def edit_web(old_address, new_address, name, markup, color, additional_style, safe_mode = false,
+ password = nil, published = false, brackets_only = false, count_pages = false,
+ allow_uploads = true, max_upload_size = nil)
+
+ if not @webs.key? old_address
+ raise Instiki::ValidationError.new("Web with address '#{old_address}' does not exist")
+ end
+
+ if old_address != new_address
+ if @webs.key? new_address
+ raise Instiki::ValidationError.new("There is already a web with address '#{new_address}'")
+ end
+ @webs[new_address] = @webs[old_address]
+ @webs.delete(old_address)
+ @webs[new_address].address = new_address
+ end
+
+ web = @webs[new_address]
+ web.refresh_revisions if settings_changed?(web, markup, safe_mode, brackets_only)
+
+ web.name, web.markup, web.color, web.additional_style, web.safe_mode =
+ name, markup, color, additional_style, safe_mode
+
+ web.password, web.published, web.brackets_only, web.count_pages =
+ password, published, brackets_only, count_pages, allow_uploads
+ web.allow_uploads, web.max_upload_size = allow_uploads, max_upload_size.to_i
+ end
+
+ def write_page(web_address, page_name, content, written_on, author)
+ page = Page.new(@webs[web_address], page_name, content, written_on, author)
+ @webs[web_address].add_page(page)
+ page
+ end
+
+ def storage_path
+ self.class.storage_path
+ end
+
+ private
+ def settings_changed?(web, markup, safe_mode, brackets_only)
+ web.markup != markup ||
+ web.safe_mode != safe_mode ||
+ web.brackets_only != brackets_only
+ end
+end
+
+class WikiService
+
+ include AbstractWikiService
+ include Madeleine::Automatic::Interceptor
+
+ # These methods do not change the state of persistent objects, and
+ # should not be ogged by Madeleine
+ automatic_read_only :authenticate, :read_page, :setup?, :webs, :storage_path, :file_yard
+
+ @@storage_path = './storage/'
+
+ class << self
+
+ def storage_path=(storage_path)
+ @@storage_path = storage_path
+ end
+
+ def storage_path
+ @@storage_path
+ end
+
+ def clean_storage
+ MadeleineServer.clean_storage(self)
+ end
+
+ def instance
+ @madeleine ||= MadeleineServer.new(self)
+ @system = @madeleine.system
+ return @system
+ end
+
+ def snapshot
+ @madeleine.snapshot
+ end
+
+ end
+
+ def initialize
+ init_wiki_service
+ end
+
+end
+
+class MadeleineServer
+
+ attr_reader :storage_path
+
+ # Clears all the command_log and snapshot files located in the storage directory, so the
+ # database is essentially dropped and recreated as blank
+ def self.clean_storage(service)
+ begin
+ Dir.foreach(service.storage_path) do |file|
+ if file =~ /(command_log|snapshot)$/
+ File.delete(File.join(service.storage_path, file))
+ end
+ end
+ rescue
+ Dir.mkdir(service.storage_path)
+ end
+ end
+
+ def initialize(service)
+ @storage_path = service.storage_path
+ @server = Madeleine::Automatic::AutomaticSnapshotMadeleine.new(service.storage_path,
+ Madeleine::ZMarshal.new) {
+ service.new
+ }
+ start_snapshot_thread
+ end
+
+ def command_log_present?
+ not Dir[storage_path + '/*.command_log'].empty?
+ end
+
+ def snapshot
+ @server.take_snapshot
+ end
+
+ def start_snapshot_thread
+ Thread.new(@server) {
+ hours_since_last_snapshot = 0
+ while true
+ begin
+ hours_since_last_snapshot += 1
+ # Take a snapshot if there is a command log, or 24 hours
+ # have passed since the last snapshot
+ if command_log_present? or hours_since_last_snapshot >= 24
+ ActionController::Base.logger.info "[#{Time.now.strftime('%Y-%m-%d %H:%M:%S')}] " +
+ 'Taking a Madeleine snapshot'
+ snapshot
+ hours_since_last_snapshot = 0
+ end
+ sleep(1.hour)
+ rescue => e
+ ActionController::Base.logger.error(e)
+ # wait for a minute (not to spoof the log with the same error)
+ # and go back into the loop, to keep trying
+ sleep(1.minute)
+ ActionController::Base.logger.info("Retrying to save a snapshot")
+ end
+ end
+ }
+ end
+
+ def system
+ @server.system
+ end
+
+end