Compare commits
No commits in common. "master" and "0.17.0" have entirely different histories.
223
CHANGELOG
Normal file → Executable file
223
CHANGELOG
Normal file → Executable file
|
@ -1,226 +1,3 @@
|
|||
N.B.: You *must* run
|
||||
|
||||
ruby bundle
|
||||
ruby bundle exec rake upgrade_instiki
|
||||
|
||||
after installing the new software, to enjoy the benefits of this new version.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
* 0.19.3
|
||||
|
||||
New Features:
|
||||
* Source view for Revisions
|
||||
* Rails updated to 2.3.14 (Security)
|
||||
* itextomml updated to 1.4.6
|
||||
* Replace REXML with Nokogiri in Maruku and
|
||||
in xhtml_safe_sanitize().
|
||||
(Huge speedup in rendering long pages)
|
||||
* MathJax updated to 1.1a final
|
||||
|
||||
Bugs Fixed:
|
||||
* Bundler upgraded to 1.0.18
|
||||
* Fix null search bug
|
||||
* Better text/html serialization (thank you, Nokogiri)
|
||||
* Fix Maruku footnote backlink (reported by Shamaoke)
|
||||
* Fix Maruku link bug
|
||||
* Fix Maruku image title bug
|
||||
* Fix Maruku hrule, email address and header bugs
|
||||
* Fix Maruku bold-in-italics bug
|
||||
* Fix Maruku empty list-item bug
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
* 0.19.2
|
||||
|
||||
New Features:
|
||||
* MathJax rendering for non-MathML capable browsers.
|
||||
* RedCloth (Textile) upgraded to 4.x (now handled
|
||||
by Bundler).
|
||||
* Bundler upgraded to 1.0.7
|
||||
* Rails updated to 2.3.11
|
||||
|
||||
Bugs Fixed:
|
||||
* Redirects and categories of included pages should
|
||||
not be inherited. (Suggestion of Andrew Stacey).
|
||||
* Bug in Maruku equation handling (reported by Andrew Stacey).
|
||||
* SVG-Edit updates and bug-fixes.
|
||||
* Bug in editing S5 slideshows.
|
||||
* Unvendor Rack
|
||||
* Fix Maruku list-parsing bug (reported by Shamaoke)
|
||||
* Validate Web address (Reported by Richard Marquez).
|
||||
* Fix a well-formedness bug
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
* 0.19.1
|
||||
|
||||
New Features:
|
||||
* From the "All" or Category listings, you can export selected pages (in any
|
||||
desired order) to a single LaTeX file.
|
||||
* LaTeX export supports \array{} (with no options) and a LaTeX-style optional
|
||||
argument for \sqrt[]{}. The latter requires itextomml 1.4.5 or later.
|
||||
* Updated to itextomml 1.4.5 (a bunch of new itex language features).
|
||||
* Updated to Rails 2.3.10. (There were security issues in 2.3.9 which, happily,
|
||||
did NOT affect Instiki 0.19. But 2.3.10 has other improvements, as well.)
|
||||
|
||||
Bugs Fixed:
|
||||
* Several SVG-Edit bugs fixed.
|
||||
* Removed some superfluous junk, to slim down the distribution (cuts the size of
|
||||
the compressed .tar.gz nearly in half).
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
* 0.19
|
||||
|
||||
New Features:
|
||||
|
||||
* WYSIWYG SVG editing (via SVG-edit)
|
||||
* One-click S5 templates
|
||||
* Itex2MML is now a Rubygem. Latest is itextomml-1.4.2.
|
||||
* Rails Metal itex endpoint
|
||||
* HTML5 support
|
||||
* Support IALs on Markdown list items
|
||||
* Updated to Rails 2.3.9 and Erubis (now at 2.6.6)
|
||||
* Updated for Rack 1.2.1, sqlite3-ruby 1.3.1
|
||||
* Manages dependencies using Bundler. Before running Instiki for the first time
|
||||
(and whenever you update), run
|
||||
|
||||
ruby bundle
|
||||
rake upgrade_instiki
|
||||
|
||||
from the instiki directory. (You may need to run
|
||||
|
||||
ruby bundle exec rake upgrade_instiki
|
||||
|
||||
instead, if you get a complaint about your version of rake.)
|
||||
|
||||
Bugs Fixed:
|
||||
|
||||
* Works with Ruby 1.9.2
|
||||
* Fixed a bug in non-Latin WikiWord processing. (Reported by Alexander Hambug)
|
||||
* Fixed Cyrillic WikiWord support.
|
||||
* More informative dnsbl lookup responses (suggested by Toby Bartels)
|
||||
* Fixed a bug in LaTeX output
|
||||
* No longer conflicts with sqlite3-ruby 1.3.x Rubygem
|
||||
* Fixed some Category listing bugs
|
||||
* Fixed an escaping bug in 'new' and 'edit' templates. (Reported by Toby Bartels)
|
||||
* Allow special characters ('.', '/', etc) in page names.
|
||||
* Fix BlahTeX/PNG path, so equations render in diff and
|
||||
previous revision pages.
|
||||
* Fix HTML Export feature so that uploaded files are
|
||||
included, stylesheets load, etc.
|
||||
* Uploaded files inclided in Markup Export.
|
||||
* Fix Print View, so that uploaded images work.
|
||||
* Fix some more Ruby 1.9 isues.
|
||||
* Prevent page from being renamed to null.
|
||||
* Fix Migration to work under PostgreSQL (from J. Zellman).
|
||||
* Updated vendored plugins
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
* 0.18.1
|
||||
|
||||
New Features:
|
||||
|
||||
* (Markdown-Extra syle) fenced codeblocks. [From Jason Blevins]
|
||||
* Fortran syntax colouring. [From Jason Blevins]
|
||||
|
||||
Bugs Fixed:
|
||||
|
||||
* Fixed some Ruby 1.9 encoding issues, with unicode page names,
|
||||
author names and categories.
|
||||
* Better display of inter-web wikilinks
|
||||
* various syntax-colouring fixes
|
||||
* Corrected length of wiki_references referenced_name (affects
|
||||
MySQL users)
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
* 0.18
|
||||
|
||||
New Features:
|
||||
|
||||
* Syntax colouring: 'ansic', 'javascript', 'sqlite', 'yaml' and 'css' modes,
|
||||
in addition to the existing 'html', 'xml', and 'ruby' modes,
|
||||
* Source view [suggested by Andrew Stacey]
|
||||
* Auto-resizing Textareas scale to fit viewing area.
|
||||
* Instiki upgraded to Rails 2.3.5 and Rack 1.1.
|
||||
* Now runs on Ruby 1.9. (If you're a Passenger user, you may need to upgrade to Passenger
|
||||
2.2.8, which works around some bugs in Ruby 1.9.1.)
|
||||
* Upgraded for itex2MML 1.3.19 (which works under Ruby 1.9, and has several new feautures,
|
||||
relative to 1.3.15).
|
||||
|
||||
Bugs Fixed:
|
||||
|
||||
* Fixed a CSS bug, which screwed up printing (unless you used the "Print" view).
|
||||
* Fixed a well-formedness bug in the page-name truncation algorithm [reported by Toby Bartels]
|
||||
* Fixed a cache-sweeping bug [reported by Toby Bartels]
|
||||
* Better accessibility.
|
||||
* Improved log rotation under Passenger.
|
||||
* Omit a (seemingly superfluous) javascript hack which causes Gecko-based browsers to request
|
||||
/my_wiki/s5/null
|
||||
when they load an s5 slideshow.
|
||||
* Upgraded vendored sqlite3-ruby and rubyzip
|
||||
* Move files when renaming a web (so that links to uploaded files don't break).
|
||||
* Many Ruby 1.9 fixes, including removing the html5lib Sanitizer.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
* 0.17.3
|
||||
|
||||
The most important facet of this release is a small change in the database
|
||||
schema. Previously, people migrating from the default SQLite3 database to MySQL
|
||||
ran the risk of silent data loss, because MySQL had a more strict interpretation
|
||||
of the column types in the database. The new schema will prevent such problems.
|
||||
|
||||
rake upgrade_instiki
|
||||
|
||||
will seamlessly upgrade your existing database to the new schema.
|
||||
|
||||
New Features:
|
||||
* Passenger support (including X-Sendfile support, if the Apache mod_xsendfile
|
||||
module is installed).
|
||||
* Update for itex2MML 1.3.13. (You should upgrade your itex2MML to the latest
|
||||
version, too.)
|
||||
|
||||
Bugs Fixed:
|
||||
* Refactored the Web model (from James Herdman).
|
||||
* Clean malformed utf-8 strings, rather than complaining about them.
|
||||
* Updated location of Textile help, since _why_the_lucky_stiff left the 'net.
|
||||
* Fixed a TeX rendering bug.
|
||||
* Updated list of XHTML+MathML named entities to match W3C Working Draft.
|
||||
* Refactored the Sanitizer (speedup).
|
||||
* Fix S5 Slideshows for non-root Instiki URLs.
|
||||
* Work around a Rails flash bug.
|
||||
* Links from published webs should work right (finally?).
|
||||
* An important database migration for MySQL users.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
* 0.17.2
|
||||
|
||||
Security: Updated to Rails 2.3.4
|
||||
* Fixes Timing Weakness in Rails MessageVerifier and the Cookie Store
|
||||
http://weblog.rubyonrails.org/2009/9/4/timing-weakness-in-ruby-on-rails
|
||||
* Fixes XSS Vulnerability in Rails
|
||||
http://weblog.rubyonrails.org/2009/9/4/xss-vulnerability-in-ruby-on-rails
|
||||
|
||||
New Features:
|
||||
* Syntax colouring (`ruby` and `html`) for code blocks.
|
||||
* Updated for itex2MML 1.3.10 (supports \rlap{} and \underline{}). You should upgrade that, too.
|
||||
* Add a "Create New Page" Link to the Search Page. (Based on an idea by nowa)
|
||||
* Updated to Rails 2.3.4
|
||||
|
||||
Bugs Fixed:
|
||||
* Wikilinks to published webs should be to the published action. This didn't work
|
||||
right for inter-web links. (Reported by Mike Shulman)
|
||||
* Use .size, rather than .length for ActiveRecord associations. A huge memory saving
|
||||
in building the recently_revised page.
|
||||
* Refactor the upgrade_instiki rake task, to make it database-agnostic. (Many thanks to James Herdman)
|
||||
* Web#files_path and Web#blatex_pngs_path now return Pathname objects. (Thanks, again, to James Herdman)
|
||||
* Workaround for Mozilla Bug 449396. (Reported by Andrew Stacey)
|
||||
* Correctly Set noindex,nofollow On /diff Pages.
|
||||
* Page-renaming javascript deals correctly with page names containing ampersands, slashes, and other garbage.
|
||||
* List of Wanted Pages should not include redirected pages.
|
||||
* The Regexp, used in Maruku to detect "email" headers (used, e.g., for S5 slideshow metadata) could, for some inputs, interact badly with Instiki's Chunk Handler. Fixed.
|
||||
* Ensure "rollback" locks page for editing.
|
||||
* Generate relative URLs, when possible. (Patch by Dennis Knauf)
|
||||
* Expire revisions of an edited page. Use a `before_save` hook to deal with the situation where a page's name has been changed.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
* 0.17
|
||||
|
||||
New features:
|
||||
|
|
12
Gemfile
12
Gemfile
|
@ -1,12 +0,0 @@
|
|||
source "http://rubygems.org"
|
||||
gem "sqlite3-ruby", :require => "sqlite3"
|
||||
gem "itextomml", ">=1.4.7"
|
||||
gem "rack", ">=1.1.0"
|
||||
gem "mongrel", ">=1.2.0.pre2"
|
||||
gem "rubyzip"
|
||||
gem "RedCloth", ">=4.0.0"
|
||||
gem "erubis"
|
||||
gem "nokogiri"
|
||||
gem "rake"
|
||||
gem "rdoc"
|
||||
gem "json"
|
79
README
Normal file → Executable file
79
README
Normal file → Executable file
|
@ -1,7 +1,7 @@
|
|||
|
||||
= Instiki
|
||||
|
||||
Instiki is a wiki clone so pretty and easy to set up, you'll wonder if it’s really a wiki. Runs on Rails and focuses on portability and stability. Supports file uploads, PDF export, RSS, multiple users and password protection. Some use Instiki as a CMS (Content Management System) because of its ability to export static pages.
|
||||
Instiki is a wiki clone so pretty and easy to set up, you'll wonder if it’s really a wiki. Runs on Rails and focuses on portability and stability. Supports file uploads, PDF export, RSS, multiple users and password protection. Some use Instiki as a CMS (Content Management System) because of it's ability to export static pages.
|
||||
|
||||
Instiki lowers the barriers of interest for when you might consider using a wiki. It's so simple to get running that you'll find yourself using it for anything -- taking notes, brainstorming, organizing a gathering.
|
||||
|
||||
|
@ -15,32 +15,33 @@ Instiki on BeOS, Amiga OS, OS2, Zeta OS and support for various exotic Platforms
|
|||
|
||||
== 3 easy Steps to get the Instiki experience
|
||||
|
||||
Step 1. Get Instiki and run "ruby bundle"
|
||||
Step 1. Get Ruby, Download Instiki
|
||||
Step 2. Run "instiki"
|
||||
Step 3. Chuckle... "There's no step three!" (TM)
|
||||
|
||||
|
||||
== Details
|
||||
|
||||
You need at least Ruby Version 1.8.6, and Rubygems 1.3.6, installed on your System. The second dependency is a Database System, but don't worry, the default sqlite3 will be installed for you, if it's not already installed. You can also use any other database system (MySQL, PostgreSQL, ...) supported by Rails.
|
||||
You need at least Ruby Version 1.8.4 installed on your System. The second dependency is a Database System, but don't worry, maybe you are already served.
|
||||
|
||||
|
||||
=== If you are on Windows
|
||||
|
||||
- Get the *Ruby One-Click Installer - Windows* http://rubyforge.org/projects/rubyinstaller
|
||||
- Get Development Kit http://github.com/oneclick/rubyinstaller/wiki/development-kit
|
||||
- In the Instiki directory, execute "ruby bundle"
|
||||
- double-click instiki.bat or instiki.cmd and there you go!
|
||||
|
||||
if you are running Windows 95, 98 or ME and cannot get instiki to run, try Version 0.11.pl1 which is the last instiki Version to support that old-style OS's. Please update to some Unix-OS or complain to the Ruby on Rails List at http://www.ruby-forum.com/forum/3 (Rails does not support your old Windows.)
|
||||
|
||||
|
||||
=== If you are on Mac OSX
|
||||
|
||||
On Leopard and Snow Leopard, you are all set.
|
||||
- run "sudo gem update --system" via the command-line.
|
||||
- run "ruby bundle" in the instiki directory.
|
||||
- run "ruby instiki" and there you go!
|
||||
On Leopard, you are all set.
|
||||
|
||||
Tiger ships with a really old Ruby Version (1.8.2) and a broken Readline Library you have to
|
||||
- use the Ruby One-Click-Installer for OSX ( http://rubyosx.com ) if you don't already have macports' Ruby
|
||||
- make sure you read http://instiki.5uper.net/instiki/show/SQLite+issues+on+OSX
|
||||
- run "ruby instiki.rb" via command-line in the directory
|
||||
|
||||
|
||||
=== If you are on Linux
|
||||
|
||||
|
@ -48,12 +49,7 @@ Tiger ships with a really old Ruby Version (1.8.2) and a broken Readline Library
|
|||
=== Any other System
|
||||
|
||||
- get Ruby for your System, compile if nessesary: http://ruby-lang.org
|
||||
- Depending on the version of Rubygems that came with your Ruby, you may need to
|
||||
|
||||
sudo gem update --system
|
||||
|
||||
- get SQLite or compile from http://sqlite.org (you can also use mysql or any other supported database system if you want)
|
||||
- run "ruby bundle"
|
||||
- run instiki
|
||||
|
||||
You're now running a perfectly suitable wiki on port 2500 that'll present you with one-step setup, followed by a textarea for the home page on http://localhost:2500
|
||||
|
@ -72,16 +68,14 @@ You're now running a perfectly suitable wiki on port 2500 that'll present you wi
|
|||
* Five markup choices:
|
||||
Markdown-based choices [http://daringfireball.net/projects/markdown/syntax]:
|
||||
Markdown+itex2MML (the default; requires itex2MML)
|
||||
Markdown+BlahTeX/PNG (requires blahtex and a working TeX installation)
|
||||
Markdown+BlahTeX/PNG (requires blahtex and a working TeX installation
|
||||
Markdown
|
||||
Textile [http://www.textism.com/tools/textile]
|
||||
RDoc [http://rdoc.sourceforge.net/doc]
|
||||
* Support for Math (using itex syntax [http://golem.ph.utexas.edu/~distler/blog/itex2MMLcommands.html])
|
||||
* Support for WYSIWYG SVG editing -- embed SVG graphics right in your wiki page.
|
||||
* Embedded webserver: uses Mongrel (if installed), or the bundled WEBrick webserver (if not).
|
||||
* Internationalization: Wiki words in any latin, greek, cyrillian, or armenian characters
|
||||
* Color diffs: Track changes through revisions
|
||||
* Runs on SQLite3 per default, can be configured to run on PostgreSQL, MySQL, DB2, Firebird, Openbase, Oracle, SQL Server or Sybase
|
||||
* Runs on SQLite per default, can be configured to run on PostgreSQL, MySQL, DB2, Firebird, Openbase, Oracle, SQL Server or Sybase
|
||||
|
||||
|
||||
== Command-line options:
|
||||
|
@ -93,19 +87,56 @@ You're now running a perfectly suitable wiki on port 2500 that'll present you wi
|
|||
|
||||
* See CHANGELOG
|
||||
|
||||
== Migrating from Instiki 0.11-0.18 to 0.19
|
||||
== Migrating from Instiki 0.11-0.12 to 0.16.3
|
||||
|
||||
rake upgrade_instiki
|
||||
|
||||
== Migrating Instiki 0.10.2 storage to Instiki 0.11.0 database
|
||||
|
||||
1. Install Instiki 0.11 and check that it works (you should be able to create a web, edit and save a HomePage)
|
||||
2. Execute
|
||||
ruby script\import_storage \
|
||||
-t /full/path/to/instiki0.10/storage \
|
||||
-i /full/path/to/instiki0.10/installation \
|
||||
-d sqlite (or mysql, or postgres, depending on what you use) \
|
||||
-o instiki_import.sql
|
||||
for example (Windows):
|
||||
ruby script\import_storage -t c:\instiki-0.10.2\storage\2500 -i c:\instiki-0.10.2 -d sqlite -o instiki_import.sql
|
||||
3. This will produce instiki_import.sql file in the current working directory.
|
||||
Open it in a text editor and inspect carefully.
|
||||
4. Connect to your production database (e.g., 'sqlite3 db\prod.db'),
|
||||
and have it execute instiki_import.sql (e.g., '.read instiki_import.sql')
|
||||
5. Execute ruby script\reset_references
|
||||
(this script parses all pages for crosslinks between them, so it may take a few minutes)
|
||||
6. Restart Instiki
|
||||
7. Go over some pages, especially those with a lot of complex markup, and see if anything is broken.
|
||||
|
||||
The most common migration problem is this: if you open All Pages and see a lot of orphaned pages,
|
||||
you forgot to run ruby script\reset_references after importing the data.
|
||||
|
||||
|
||||
===Upgrading from Instiki-AR Beta 1
|
||||
|
||||
In Beta 2, we switch to ActiveRecord:Migrations. Therefore:
|
||||
1. Back up your production database.
|
||||
2. Open command-line session to your database and execute:
|
||||
create table schema_info (version integer(11));
|
||||
insert into schema_info (version) values (1);
|
||||
3. Go back to the shell, change directory to the new Instiki and execute "rake migrate".
|
||||
|
||||
Step 2 creates a table that tells to ActiveRecord:Migrations that the current version
|
||||
of this database is 1 (corresponding to Beta 1), and step 3 makes it up-to-date with
|
||||
the current version of Instiki.
|
||||
|
||||
|
||||
ruby bundle
|
||||
ruby bundle exec rake upgrade_instiki
|
||||
|
||||
== Download the latest release from:
|
||||
|
||||
* http://rubyforge.org/project/showfiles.php?group_id=186
|
||||
|
||||
|
||||
== Visit the Instiki wiki:
|
||||
== Visit the "official" Instiki wiki:
|
||||
|
||||
* http://golem.ph.utexas.edu/wiki/instiki/
|
||||
* http://instiki.org
|
||||
|
||||
|
||||
== License:
|
||||
|
|
21
UPGRADING
21
UPGRADING
|
@ -1,21 +0,0 @@
|
|||
UPGRADING
|
||||
=========
|
||||
|
||||
See the upgrading instructions
|
||||
|
||||
http://golem.ph.utexas.edu/wiki/instiki/show/Upgrading
|
||||
|
||||
for detailed instructions.
|
||||
|
||||
At a minimum, you need to backup your database.
|
||||
After installing the new software and restoring your database, you need to run
|
||||
|
||||
ruby bundle
|
||||
ruby bundle exec rake upgrade_instiki
|
||||
|
||||
from the commandline, to complete the upgrade. Doing a
|
||||
|
||||
|
||||
ruby bundle update
|
||||
|
||||
will update the installed gems (used by Instiki) to the latest versions.
|
0
app/apis/.gitignore
vendored
0
app/apis/.gitignore
vendored
|
@ -138,4 +138,15 @@ class AdminController < ApplicationController
|
|||
redirect_to :back
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def is_post
|
||||
unless (request.post? || ENV["RAILS_ENV"] == "test")
|
||||
headers['Allow'] = 'POST'
|
||||
render(:status => 405, :text => 'You must use an HTTP POST', :layout => 'error')
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# The filters added to this controller will be run for all controllers in the application.
|
||||
# Likewise will all the methods added be available for all controllers.
|
||||
class ApplicationController < ActionController::Base
|
||||
|
||||
# require 'dnsbl_check'
|
||||
protect_forms_from_spam
|
||||
before_filter :connect_to_model, :check_authorization, :setup_url_generator, :set_content_type_header, :set_robots_metatag
|
||||
after_filter :remember_location, :teardown_url_generator
|
||||
|
@ -19,31 +19,13 @@ class ApplicationController < ActionController::Base
|
|||
Wiki.new
|
||||
end
|
||||
|
||||
helper_method :xhtml_enabled?, :html_ext, :darken
|
||||
|
||||
protected
|
||||
|
||||
def xhtml_enabled?
|
||||
in_a_web? and [:markdownMML, :markdownPNG, :markdown].include?(@web.markup)
|
||||
end
|
||||
|
||||
def html_ext
|
||||
if xhtml_enabled? && request.env.include?('HTTP_ACCEPT') &&
|
||||
Mime::Type.parse(request.env["HTTP_ACCEPT"]).include?(Mime::XHTML)
|
||||
'xhtml'
|
||||
else
|
||||
'html'
|
||||
end
|
||||
end
|
||||
|
||||
def darken(s)
|
||||
n=s.length/3
|
||||
s.scan( %r(\w{#{n},#{n}}) ).collect {|a| (a.hex * 2/3).to_s(16).rjust(n,'0')}.join
|
||||
end
|
||||
|
||||
def check_authorization
|
||||
redirect_to(:controller => 'wiki', :action => 'login',
|
||||
:web => @web_name) if in_a_web? and authorization_needed? and not authorized?
|
||||
def check_authorization
|
||||
if in_a_web? and authorization_needed? and not authorized?
|
||||
redirect_to :controller => 'wiki', :action => 'login', :web => @web_name
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def connect_to_model
|
||||
|
@ -53,8 +35,10 @@ class ApplicationController < ActionController::Base
|
|||
@author = cookies['author'] || 'AnonymousCoward'
|
||||
if @web_name
|
||||
@web = @wiki.webs[@web_name]
|
||||
render(:status => 404, :text => "Unknown web '#{@web_name}'",
|
||||
:layout => 'error') if @web.nil?
|
||||
if @web.nil?
|
||||
render(:status => 404, :text => "Unknown web '#{@web_name}'", :layout => 'error')
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -103,8 +87,7 @@ class ApplicationController < ActionController::Base
|
|||
original_options[:type] ||= (FILE_TYPES[File.extname(file_name)] or 'application/octet-stream')
|
||||
original_options[:disposition] ||= (DISPOSITION[original_options[:type]] or 'attachment')
|
||||
original_options[:stream] ||= false
|
||||
original_options[:x_sendfile] = true if request.env.include?('HTTP_X_SENDFILE_TYPE') &&
|
||||
( request.remote_addr == LOCALHOST || defined?(PhusionPassenger) )
|
||||
original_options[:x_sendfile] = true if request.env.include?('HTTP_X_SENDFILE_TYPE') && request.remote_addr == LOCALHOST
|
||||
original_options
|
||||
end
|
||||
|
||||
|
@ -157,7 +140,7 @@ class ApplicationController < ActionController::Base
|
|||
<html xmlns="http://www.w3.org/1999/xhtml"><body>
|
||||
<h2>Internal Error</h2>
|
||||
<p>An application error occurred while processing your request.</p>
|
||||
<!-- \n#{exception.to_s.purify.gsub!(/-{2,}/, '- -') }\n#{exception.backtrace.join("\n")}\n -->
|
||||
<!-- \n#{exception.to_s.is_utf8? ? exception.to_s.gsub!(/-{2,}/, '- -') : ''}\n#{exception.backtrace.join("\n")}\n -->
|
||||
</body></html>
|
||||
EOL
|
||||
end
|
||||
|
@ -182,11 +165,13 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
end
|
||||
|
||||
public
|
||||
|
||||
def set_content_type_header
|
||||
response.charset = 'utf-8'
|
||||
if %w(atom_with_content atom_with_headlines).include?(action_name)
|
||||
response.content_type = Mime::ATOM
|
||||
elsif %w(tex tex_list).include?(action_name)
|
||||
elsif %w(tex).include?(action_name)
|
||||
response.content_type = Mime::TEXT
|
||||
elsif xhtml_enabled?
|
||||
if request.user_agent =~ /Validator/ or request.env.include?('HTTP_ACCEPT') &&
|
||||
|
@ -204,8 +189,14 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
end
|
||||
|
||||
def xhtml_enabled?
|
||||
in_a_web? and (@web.markup == :markdownMML or @web.markup == :markdownPNG or @web.markup == :markdown)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_robots_metatag
|
||||
if controller_name == 'wiki' and %w(show published s5).include? action_name and !(params[:mode] == 'diff')
|
||||
if controller_name == 'wiki' and %w(show published).include? action_name
|
||||
@robots_metatag_value = 'index,follow'
|
||||
else
|
||||
@robots_metatag_value = 'noindex,nofollow'
|
||||
|
@ -242,16 +233,6 @@ class ApplicationController < ActionController::Base
|
|||
(@web.published? and action_name == 's5')
|
||||
end
|
||||
|
||||
def is_post
|
||||
unless (request.post? || Rails.env.test?)
|
||||
layout = 'error'
|
||||
layout = false if %w(tex tex_list).include?(action_name)
|
||||
headers['Allow'] = 'POST'
|
||||
render(:status => 405, :text => 'You must use an HTTP POST', :layout => layout)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
module Mime
|
||||
|
@ -276,10 +257,10 @@ end
|
|||
module Instiki
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 0
|
||||
MINOR = 19
|
||||
TINY = 3
|
||||
MINOR = 17
|
||||
TINY = 0
|
||||
SUFFIX = '(MML+)'
|
||||
PRERELEASE = false
|
||||
PRERELEASE = false
|
||||
if PRERELEASE
|
||||
STRING = [MAJOR, MINOR].join('.') + PRERELEASE + SUFFIX
|
||||
else
|
||||
|
@ -287,22 +268,3 @@ module Instiki
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Monkey patch, to make Hash#key work in Ruby 1.8
|
||||
class Hash
|
||||
alias_method(:key, :index) unless method_defined?(:key)
|
||||
end
|
||||
|
||||
# Monkey patch, to ensure ActionCache doesn't muck with the content-type header.
|
||||
module ActionController #:nodoc:
|
||||
module Caching
|
||||
module Actions
|
||||
class ActionCacheFilter
|
||||
private
|
||||
def set_content_type!(controller, extension)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -2,17 +2,17 @@ module CacheSweepingHelper
|
|||
|
||||
def expire_cached_page(web, page_name)
|
||||
expire_action :controller => 'wiki', :web => web.address,
|
||||
:action => %w(show published s5 tex print history source), :id => page_name
|
||||
:action => %w(show published s5 tex print history), :id => page_name
|
||||
expire_action :controller => 'wiki', :web => web.address,
|
||||
:action => 'show', :id => page_name, :mode => 'diff'
|
||||
end
|
||||
|
||||
def expire_cached_summary_pages(web)
|
||||
categories = WikiReference.list_categories(web)
|
||||
categories = WikiReference.all(:conditions => "link_type = 'C'")
|
||||
%w(recently_revised list).each do |action|
|
||||
expire_action :controller => 'wiki', :web => web.address, :action => action
|
||||
categories.each do |category|
|
||||
expire_action :controller => 'wiki', :web => web.address, :action => action, :category => category
|
||||
expire_action :controller => 'wiki', :web => web.address, :action => action, :category => category.referenced_name
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -26,14 +26,12 @@ module CacheSweepingHelper
|
|||
end
|
||||
|
||||
def expire_cached_revisions(page)
|
||||
page.rev_ids.count.times do |i|
|
||||
page.revisions.length.times do |i|
|
||||
revno = i+1
|
||||
expire_action :controller => 'wiki', :web => page.web.address,
|
||||
:action => 'revision', :id => page.name, :rev => revno
|
||||
expire_action :controller => 'wiki', :web => page.web.address,
|
||||
:action => 'revision', :id => page.name, :rev => revno, :mode => 'diff'
|
||||
expire_action :controller => 'wiki', :web => page.web.address,
|
||||
:action => 'source', :id => page.name, :rev => revno
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
# Controller responsible for serving files and pictures.
|
||||
|
||||
require 'zip/zip'
|
||||
require 'instiki_stringsupport'
|
||||
require 'stringsupport'
|
||||
|
||||
class FileController < ApplicationController
|
||||
|
||||
layout 'default'
|
||||
|
||||
before_filter :check_authorized
|
||||
before_filter :check_allow_uploads, :dnsbl_check, :except => [:file, :blahtex_png]
|
||||
before_filter :dnsbl_check, :check_authorized
|
||||
before_filter :check_allow_uploads, :except => [:file, :blahtex_png]
|
||||
|
||||
def file
|
||||
@file_name = params['id']
|
||||
|
@ -18,9 +18,6 @@ class FileController < ApplicationController
|
|||
new_file = @web.wiki_files.create(params['file'])
|
||||
if new_file.valid?
|
||||
flash[:info] = "File '#{@file_name}' successfully uploaded"
|
||||
WikiReference.pages_that_link_to_file(@web, @file_name).each do |page|
|
||||
RevisionSweeper.expire_page(@web, page)
|
||||
end
|
||||
redirect_to(params['referring_page'])
|
||||
else
|
||||
# pass the file with errors back into the form
|
||||
|
@ -29,7 +26,7 @@ class FileController < ApplicationController
|
|||
end
|
||||
else
|
||||
# no form supplied, this is a request to download the file
|
||||
file = @web.files_path.join(@file_name)
|
||||
file = @web.files_path + '/' + @file_name
|
||||
if File.exists?(file)
|
||||
send_file(file)
|
||||
else
|
||||
|
@ -41,7 +38,7 @@ class FileController < ApplicationController
|
|||
end
|
||||
|
||||
def blahtex_png
|
||||
send_file(@web.blahtex_pngs_path.join(params['id']))
|
||||
send_file(@web.blahtex_pngs_path + '/' + params['id'])
|
||||
end
|
||||
|
||||
def delete
|
||||
|
@ -91,37 +88,50 @@ class FileController < ApplicationController
|
|||
protected
|
||||
|
||||
def check_authorized
|
||||
unless authorized? or @web.published?
|
||||
if authorized? or @web.published?
|
||||
return true
|
||||
else
|
||||
@hide_navigation = true
|
||||
render(:status => 403, :text => 'This web is private', :layout => true)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
def check_allow_uploads
|
||||
if @web
|
||||
if @web.allow_uploads? and authorized?
|
||||
return true
|
||||
else
|
||||
@hide_navigation = true
|
||||
render(:status => 403, :text => 'File uploads are blocked by the webmaster', :layout => true)
|
||||
return false
|
||||
end
|
||||
render(:status => 404, :text => "Web #{params['web'].inspect} not found", :layout => 'error') and return false unless @web
|
||||
if @web.allow_uploads? and authorized?
|
||||
return true
|
||||
else
|
||||
render(:status => 404, :text => "Web #{params['web'].inspect} not found", :layout => 'error')
|
||||
@hide_navigation = true
|
||||
render(:status => 403, :text => 'File uploads are blocked by the webmaster', :layout => true)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def is_post
|
||||
unless (request.post? || ENV["RAILS_ENV"] == "test")
|
||||
headers['Allow'] = 'POST'
|
||||
render(:status => 405, :text => 'You must use an HTTP POST', :layout => 'error')
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
def import_from_archive(archive)
|
||||
logger.info "Importing pages from #{archive}"
|
||||
zip = Zip::ZipInputStream.open(archive)
|
||||
while (entry = zip.get_next_entry) do
|
||||
ext_length = File.extname(entry.name).length
|
||||
page_name = entry.name[0..-(ext_length + 1)].purify
|
||||
page_content = entry.get_input_stream.read.purify
|
||||
page_name = entry.name[0..-(ext_length + 1)]
|
||||
page_content = entry.get_input_stream.read
|
||||
logger.info "Processing page '#{page_name}'"
|
||||
begin
|
||||
if !page_content.is_utf8?
|
||||
logger.info "Page '#{page_name}' contains non-utf8 character data. Skipping."
|
||||
next
|
||||
end
|
||||
existing_page = @wiki.read_page(@web.address, page_name)
|
||||
if existing_page
|
||||
if existing_page.content == page_content
|
||||
|
|
|
@ -6,42 +6,23 @@ class RevisionSweeper < ActionController::Caching::Sweeper
|
|||
|
||||
observe Revision, Page
|
||||
|
||||
def before_save(record)
|
||||
if record.is_a?(Revision)
|
||||
expire_cached_page(record.page.web, record.page.name)
|
||||
expire_cached_revisions(record.page)
|
||||
end
|
||||
end
|
||||
|
||||
def after_save(record)
|
||||
if record.is_a?(Revision)
|
||||
expire_caches(record.page)
|
||||
end
|
||||
end
|
||||
|
||||
def after_create(record)
|
||||
if record.is_a?(Page)
|
||||
WikiReference.pages_that_reference(record.web, record.name).each do |page_name|
|
||||
expire_cached_page(record.web, page_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def after_delete(record)
|
||||
if record.is_a?(Page)
|
||||
expire_caches(record)
|
||||
end
|
||||
end
|
||||
|
||||
def self.expire_page(web, page_name)
|
||||
new.expire_cached_page(web, page_name)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def expire_caches(page)
|
||||
expire_cached_summary_pages(page.web)
|
||||
pages_to_expire = ([page.name] +
|
||||
pages_to_expire = ([page.name] + WikiReference.pages_that_reference(page.web, page.name) +
|
||||
WikiReference.pages_redirected_to(page.web, page.name) +
|
||||
WikiReference.pages_that_include(page.web, page.name)).uniq
|
||||
pages_to_expire.each { |page_name| expire_cached_page(page.web, page_name) }
|
||||
|
|
|
@ -9,7 +9,7 @@ class WebSweeper < ActionController::Caching::Sweeper
|
|||
def after_save(record)
|
||||
if record.is_a?(Web)
|
||||
web = record
|
||||
web.pages.find_each { |page| expire_cached_page(web, page.name) }
|
||||
web.pages.each { |page| expire_cached_page(web, page.name) }
|
||||
expire_cached_summary_pages(web)
|
||||
elsif record.is_a?(WikiFile)
|
||||
record.web.pages_that_link_to_file(record.file_name).each do |page|
|
||||
|
|
|
@ -1,26 +1,25 @@
|
|||
require 'fileutils'
|
||||
require 'maruku'
|
||||
require 'maruku/ext/math'
|
||||
require 'zip/zip'
|
||||
require 'instiki_stringsupport'
|
||||
require 'stringsupport'
|
||||
require 'resolv'
|
||||
|
||||
class WikiController < ApplicationController
|
||||
|
||||
before_filter :load_page
|
||||
before_filter :dnsbl_check, :only => [:edit, :new, :save, :export_html, :export_markup]
|
||||
caches_action :show, :published, :authors, :tex, :s5, :print, :recently_revised, :list, :file_list, :source,
|
||||
caches_action :show, :published, :authors, :tex, :s5, :print, :recently_revised, :list, :file_list,
|
||||
:history, :revision, :atom_with_content, :atom_with_headlines, :if => Proc.new { |c| c.send(:do_caching?) }
|
||||
cache_sweeper :revision_sweeper
|
||||
|
||||
layout 'default', :except => [:atom_with_content, :atom_with_headlines, :atom, :source, :tex, :s5, :export_html]
|
||||
layout 'default', :except => [:atom_with_content, :atom_with_headlines, :atom, :tex, :s5, :export_html]
|
||||
|
||||
def index
|
||||
if @web_name
|
||||
redirect_home
|
||||
elsif not @wiki.setup?
|
||||
redirect_to :controller => 'admin', :action => 'create_system'
|
||||
elsif @wiki.webs.size == 1
|
||||
elsif @wiki.webs.length == 1
|
||||
redirect_home @wiki.webs.values.first.address
|
||||
else
|
||||
redirect_to :action => 'web_list'
|
||||
|
@ -68,65 +67,42 @@ class WikiController < ApplicationController
|
|||
end
|
||||
|
||||
def export_html
|
||||
stylesheet = File.read(File.join(RAILS_ROOT, 'public', 'stylesheets', 'instiki.css'))
|
||||
export_pages_as_zip(html_ext) do |page|
|
||||
renderer = PageRenderer.new(page.current_revision)
|
||||
|
||||
renderer = PageRenderer.new(page.revisions.last)
|
||||
rendered_page = <<-EOL
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg-flat.dtd" >
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>#{page.plain_name} in #{@web.name}</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>#{page.plain_name} in #{@web.name}</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
|
||||
<script src="public/javascripts/page_helper.js" type="text/javascript"></script>
|
||||
<link href="public/stylesheets/instiki.css" media="all" rel="stylesheet" type="text/css" />
|
||||
<link href="public/stylesheets/syntax.css" media="all" rel="stylesheet" type="text/css" />
|
||||
<style type="text/css">
|
||||
h1#pageName, div.info, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, [actiontype="toggle"]:hover, #TextileHelp h3 {
|
||||
color: ##{@web ? @web.color : "393"};
|
||||
}
|
||||
a:visited.existingWikiWord {
|
||||
color: ##{darken(@web ? @web.color : "393")};
|
||||
}
|
||||
</style>
|
||||
|
||||
<style type="text/css"><!--/*--><![CDATA[/*><!--*/
|
||||
#{@web ? @web.additional_style : ''}
|
||||
/*]]>*/--></style>
|
||||
<script src="public/javascripts/prototype.js" type="text/javascript"></script>
|
||||
<script src="public/javascripts/effects.js" type="text/javascript"></script>
|
||||
<script src="public/javascripts/dragdrop.js" type="text/javascript"></script>
|
||||
<script src="public/javascripts/controls.js" type="text/javascript"></script>
|
||||
<script src="public/javascripts/application.js" type="text/javascript"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div id="Container">
|
||||
<div id="Content">
|
||||
<h1 id="pageName">
|
||||
#{xhtml_enabled? ? %{<span id="svg_logo"><svg version="1.1" width="100%" height="100%" viewBox='0 -1 180 198' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path id="svg_logo_path" fill="##{@web ? @web.color : "393"}" stroke-width='0.5' stroke='#000' d='
|
||||
M170,60c4,11-1,20-12,25c-9,4-25,3-20,15c5,5,15,0,24,1c11,1,21,11,14,21c-10,15-35,6-48-1c-5-3-27-23-32-10c-1,13,15,10,22,16
|
||||
c11,4,24,14,34,20c12,10,7,25-9,23c-11-1-22-9-30-16c-5-5-13-18-21-9c-2,6,2,11,5,14c9,9,22,14,22,31c-2,8-12,8-18,4c-4-3-9-8-11-13
|
||||
c-3-6-5-18-12-18c-14-1-5,28-18,30c-9,2-13-9-12-16c1-14,12-24,21-31c5-4,17-13,10-20c-9-10-19,12-23,16c-7,7-17,16-31,15
|
||||
c-9-1-18-9-11-17c5-7,14-4,23-6c6-1,15-8,8-15c-5-6-57,2-42-24c7-12,51,4,61,6c6,1,17,4,18-4c2-11-12-7-21-8c-21-2-49-14-49-34
|
||||
c0-5,3-11,8-11C31,42,34,65,42,67c6,1,9-3,8-9C49,49,38,40,40,25c1-5,4-15,13-14c10,2,11,18,13,29c1,8,0,24,7,28c15,0,5-22,4-30
|
||||
C74,23,78,7,87,1c8-4,14,1,16,9c2,11-8,21-2,30c8,2,11-6,14-12c9-14,36-18,30,5c-3,9-12,19-21,24c-6,4-22,10-23,19c-2,14,15,2,18-2
|
||||
c9-9,20-18,33-22C159,52,166,54,170,60' />
|
||||
</svg></span>} : ''}
|
||||
<span class="webName">#{@web.name}</span><br />
|
||||
#{page.plain_name}
|
||||
</h1>
|
||||
#{renderer.display_content_for_export}
|
||||
<div class="byline">
|
||||
#{page.revisions? ? "Revised" : "Created" } on #{ page.revised_at.strftime('%B %d, %Y %H:%M:%S') }
|
||||
by
|
||||
#{ UrlGenerator.new(self).make_link(@web, page.author.name, @web, nil, { :mode => :export }) }
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
EOL
|
||||
<style type="text/css">
|
||||
h1#pageName, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover {
|
||||
color: ##{@web ? @web.color : "393" };
|
||||
}
|
||||
.newWikiWord { background-color: white; font-style: italic; }
|
||||
#{stylesheet}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
#{@web.additional_style}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="pageName">
|
||||
<span class="webName">#{@web.name}</span><br />
|
||||
#{page.plain_name}
|
||||
</h1>
|
||||
#{renderer.display_content_for_export}
|
||||
<div class="byline">
|
||||
#{page.revisions? ? "Revised" : "Created" } on #{ page.revised_at.strftime('%B %d, %Y %H:%M:%S') }
|
||||
by
|
||||
#{ UrlGenerator.new(self).make_link(page.author.name, @web, nil, { :mode => :export }) }
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
EOL
|
||||
rendered_page
|
||||
end
|
||||
end
|
||||
|
@ -184,34 +160,10 @@ EOL
|
|||
def atom_with_headlines
|
||||
render_atom(hide_description = true)
|
||||
end
|
||||
|
||||
def tex_list
|
||||
return unless is_post
|
||||
if [:markdownMML, :markdownPNG, :markdown].include?(@web.markup)
|
||||
@tex_content = ''
|
||||
# Ruby 1.9.x has ordered hashes; 1.8.x doesn't. So let's just parse the query ourselves.
|
||||
ordered_params = ActiveSupport::OrderedHash[*request.raw_post.split('&').collect {|k_v| k_v.split('=').collect {|x| CGI::unescape(x)}}.flatten]
|
||||
ordered_params.each do |name, p|
|
||||
if p == 'tex' && @web.has_page?(name)
|
||||
@tex_content << "\\section*\{#{Maruku.new(name).to_latex.strip}\}\n\n"
|
||||
@tex_content << Maruku.new(@web.page(name).content).to_latex
|
||||
end
|
||||
end
|
||||
else
|
||||
@tex_content = 'TeX export only supported with the Markdown text filters.'
|
||||
end
|
||||
if @tex_content == ''
|
||||
flash[:error] = "You didn't select any pages to export."
|
||||
redirect_to :back
|
||||
return
|
||||
end
|
||||
expire_action :controller => 'wiki', :web => @web.address, :action => 'list', :category => params['category']
|
||||
render(:layout => 'tex')
|
||||
end
|
||||
|
||||
|
||||
def search
|
||||
@query = params['query'] ? params['query'].purify : ''
|
||||
@query = params['query']
|
||||
render(:text => "Your query string was not valid utf-8", :layout => 'error', :status => 400) and return unless @query.is_utf8?
|
||||
@title_results = @web.select { |page| page.name =~ /#{@query}/i }.sort
|
||||
@results = @web.select { |page| page.content =~ /#{@query}/i }.sort
|
||||
all_pages_found = (@results + @title_results).uniq
|
||||
|
@ -228,7 +180,7 @@ EOL
|
|||
end
|
||||
|
||||
def edit
|
||||
if @page.nil?
|
||||
if @page.nil? or not @page_name.is_utf8?
|
||||
redirect_home
|
||||
elsif @page.locked?(Time.now) and not params['break_lock']
|
||||
redirect_to :web => @web_name, :action => 'locked', :id => @page_name
|
||||
|
@ -238,10 +190,12 @@ EOL
|
|||
end
|
||||
|
||||
def locked
|
||||
render(:text => 'Page name is not valid utf-8.', :status => 400, :layout => 'error') unless @page_name.is_utf8?
|
||||
# to template
|
||||
end
|
||||
|
||||
def new
|
||||
render(:text => 'Page name is not valid utf-8.', :status => 400, :layout => 'error') unless @page_name.is_utf8?
|
||||
# to template
|
||||
end
|
||||
|
||||
|
@ -262,7 +216,7 @@ EOL
|
|||
redirect_home
|
||||
end
|
||||
@link_mode ||= :show
|
||||
@renderer = PageRenderer.new(@page.current_revision)
|
||||
@renderer = PageRenderer.new(@page.revisions.last)
|
||||
# to template
|
||||
end
|
||||
|
||||
|
@ -276,7 +230,7 @@ EOL
|
|||
@page ||= wiki.read_page(@web_name, @page_name)
|
||||
@link_mode ||= :publish
|
||||
if @page
|
||||
@renderer = PageRenderer.new(@page.current_revision)
|
||||
@renderer = PageRenderer.new(@page.revisions.last)
|
||||
else
|
||||
real_page = WikiReference.page_that_redirects_for(@web, @page_name)
|
||||
if real_page
|
||||
|
@ -296,30 +250,37 @@ EOL
|
|||
|
||||
def rollback
|
||||
get_page_and_revision
|
||||
if @page.locked?(Time.now) and not params['break_lock']
|
||||
redirect_to :web => @web_name, :action => 'locked', :id => @page_name
|
||||
else
|
||||
@page.lock(Time.now, @author)
|
||||
end
|
||||
end
|
||||
|
||||
def save
|
||||
render(:status => 404, :text => 'Undefined page name', :layout => 'error') and return if @page_name.nil?
|
||||
return unless is_post
|
||||
author_name = params['author'].purify
|
||||
render(:status => 404, :text => 'Undefined page name', :layout => 'error') and return if @page_name.nil? or not @page_name.is_utf8?
|
||||
unless (request.post? || ENV["RAILS_ENV"] == "test")
|
||||
headers['Allow'] = 'POST'
|
||||
render(:status => 405, :text => 'You must use an HTTP POST', :layout => 'error')
|
||||
return
|
||||
end
|
||||
author_name = params['author']
|
||||
author_name = 'AnonymousCoward' if author_name =~ /^\s*$/
|
||||
|
||||
begin
|
||||
the_content = params['content'].purify
|
||||
prev_content = ''
|
||||
raise Instiki::ValidationError.new('Your name was not valid utf-8') unless author_name.is_utf8?
|
||||
raise Instiki::ValidationError.new('Your name cannot contain a "."') if author_name.include? '.'
|
||||
cookies['author'] = { :value => author_name, :expires => Time.utc(2030) }
|
||||
the_content = params['content']
|
||||
filter_spam(the_content)
|
||||
cookies['author'] = { :value => author_name.dup.as_bytes, :expires => Time.utc(2030) }
|
||||
unless the_content.is_utf8?
|
||||
if @page
|
||||
the_content = @page.content
|
||||
else
|
||||
the_content = ''
|
||||
end
|
||||
raise Instiki::ValidationError.new('Your content was not valid utf-8.')
|
||||
end
|
||||
if @page
|
||||
new_name = params['new_name'] ? params['new_name'].purify : @page_name
|
||||
new_name = @page_name if new_name.empty?
|
||||
prev_content = @page.current_revision.content
|
||||
raise Instiki::ValidationError.new('A page named "' + new_name.escapeHTML + '" already exists.') if
|
||||
@page_name != new_name && @web.has_page?(new_name)
|
||||
new_name = params['new_name'] || @page_name
|
||||
raise Instiki::ValidationError.new('Your new title was not valid utf-8.') unless new_name.is_utf8?
|
||||
raise Instiki::ValidationError.new('Your new title cannot contain a "."') if new_name.include? '.'
|
||||
raise Instiki::ValidationError.new('A page named "' + new_name.escapeHTML + '" already exists.') if @page_name != new_name && @web.has_page?(new_name)
|
||||
wiki.revise_page(@web_name, @page_name, new_name, the_content, Time.now,
|
||||
Author.new(author_name, remote_ip), PageRenderer.new)
|
||||
@page.unlock
|
||||
|
@ -332,15 +293,11 @@ EOL
|
|||
rescue Instiki::ValidationError => e
|
||||
flash[:error] = e.to_s
|
||||
logger.error e
|
||||
param_hash = {:web => @web_name, :id => @page_name}
|
||||
# Work around Rails bug: flash will not display if query string is longer than 10192 bytes
|
||||
param_hash.update( :content => the_content ) if the_content &&
|
||||
CGI::escape(the_content).length < 10183 && the_content != prev_content
|
||||
if @page
|
||||
@page.unlock
|
||||
redirect_to param_hash.update( :action => 'edit' )
|
||||
redirect_to :action => 'edit', :web => @web_name, :id => @page_name, :content => the_content
|
||||
else
|
||||
redirect_to param_hash.update( :action => 'new' )
|
||||
redirect_to :action => 'new', :web => @web_name, :id => @page_name, :content => the_content
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -348,7 +305,7 @@ EOL
|
|||
def show
|
||||
if @page
|
||||
begin
|
||||
@renderer = PageRenderer.new(@page.current_revision)
|
||||
@renderer = PageRenderer.new(@page.revisions.last)
|
||||
@show_diff = (params[:mode] == 'diff')
|
||||
render :action => 'page'
|
||||
# TODO this rescue should differentiate between errors due to rendering and errors in
|
||||
|
@ -363,7 +320,7 @@ EOL
|
|||
end
|
||||
end
|
||||
else
|
||||
if not @page_name.nil? and not @page_name.empty?
|
||||
if not @page_name.nil? and @page_name.is_utf8? and not @page_name.empty?
|
||||
real_page = WikiReference.page_that_redirects_for(@web, @page_name)
|
||||
if real_page
|
||||
flash[:info] = "Redirected from \"#{@page_name}\"."
|
||||
|
@ -383,8 +340,8 @@ EOL
|
|||
if @page
|
||||
@revisions_by_day = Hash.new { |h, day| h[day] = [] }
|
||||
@revision_numbers = Hash.new { |h, id| h[id] = [] }
|
||||
revision_number = @page.rev_ids.size
|
||||
@page.rev_ids.reverse.each do |rev|
|
||||
revision_number = @page.revisions.length
|
||||
@page.revisions.reverse.each do |rev|
|
||||
day = Date.new(rev.revised_at.year, rev.revised_at.month, rev.revised_at.day)
|
||||
@revisions_by_day[day] << rev
|
||||
@revision_numbers[rev.id] = revision_number
|
||||
|
@ -392,7 +349,7 @@ EOL
|
|||
end
|
||||
render :action => 'history'
|
||||
else
|
||||
if not @page_name.nil? and not @page_name.empty?
|
||||
if not @page_name.nil? and @page_name.is_utf8? and not @page_name.empty?
|
||||
redirect_to :web => @web_name, :action => 'new', :id => @page_name
|
||||
else
|
||||
render :text => 'Page name is not specified', :status => 404, :layout => 'error'
|
||||
|
@ -400,22 +357,17 @@ EOL
|
|||
end
|
||||
end
|
||||
|
||||
def source
|
||||
@revision = @page.revisions[params['rev'].to_i - 1] if params['rev']
|
||||
end
|
||||
|
||||
def tex
|
||||
if [:markdownMML, :markdownPNG, :markdown].include?(@web.markup)
|
||||
if @web.markup == :markdownMML or @web.markup == :markdownPNG or @web.markup == :markdown
|
||||
@tex_content = Maruku.new(@page.content).to_latex
|
||||
else
|
||||
@tex_content = 'TeX export only supported with the Markdown text filters.'
|
||||
end
|
||||
render(:layout => 'tex')
|
||||
end
|
||||
|
||||
def s5
|
||||
if [:markdownMML, :markdownPNG, :markdown].include?(@web.markup)
|
||||
my_rendered = PageRenderer.new(@page.current_revision)
|
||||
if @web.markup == :markdownMML || @web.markup == :markdownPNG || @web.markup == :markdown
|
||||
my_rendered = PageRenderer.new(@page.revisions.last)
|
||||
@s5_content = my_rendered.display_s5
|
||||
@s5_theme = my_rendered.s5_theme
|
||||
else
|
||||
|
@ -424,6 +376,15 @@ EOL
|
|||
end
|
||||
end
|
||||
|
||||
def html_ext
|
||||
if xhtml_enabled? && request.env.include?('HTTP_ACCEPT') &&
|
||||
Mime::Type.parse(request.env["HTTP_ACCEPT"]).include?(Mime::XHTML)
|
||||
'xhtml'
|
||||
else
|
||||
'html'
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def do_caching?
|
||||
|
@ -431,7 +392,7 @@ EOL
|
|||
end
|
||||
|
||||
def load_page
|
||||
@page_name = params['id'] ? params['id'].purify : nil
|
||||
@page_name = params['id']
|
||||
@page = @wiki.read_page(@web_name, @page_name) if @page_name
|
||||
end
|
||||
|
||||
|
@ -462,32 +423,22 @@ EOL
|
|||
|
||||
file_prefix = "#{@web.address}-#{file_type}-"
|
||||
timestamp = @web.revised_at.strftime('%Y-%m-%d-%H-%M-%S')
|
||||
file_path = @wiki.storage_path.join(file_prefix + timestamp + '.zip')
|
||||
file_path = File.join(@wiki.storage_path, file_prefix + timestamp + '.zip')
|
||||
tmp_path = "#{file_path}.tmp"
|
||||
|
||||
Zip::ZipFile.open(tmp_path, Zip::ZipFile::CREATE) do |zip_out|
|
||||
Zip::ZipOutputStream.open(tmp_path) do |zip_out|
|
||||
@web.select.by_name.each do |page|
|
||||
zip_out.get_output_stream("#{CGI.escape(page.name)}.#{file_type}") do |f|
|
||||
f.puts(block.call(page))
|
||||
end
|
||||
zip_out.put_next_entry("#{CGI.escape(page.name)}.#{file_type}")
|
||||
zip_out.puts(block.call(page))
|
||||
end
|
||||
# add an index file, and the stylesheet and javascript directories, if exporting to HTML
|
||||
# add an index file, if exporting to HTML
|
||||
if file_type.to_s.downcase == html_ext
|
||||
zip_out.get_output_stream("index.#{html_ext}") do |f|
|
||||
f.puts "<html xmlns='http://www.w3.org/1999/xhtml'><head>" +
|
||||
"<meta http-equiv=\"Refresh\" content=\"0;URL=HomePage.#{html_ext}\" /></head></html>"
|
||||
end
|
||||
dir = Rails.root.join('public')
|
||||
Dir["#{dir}/{images,javascripts,s5,stylesheets}/**/*"].each do |f|
|
||||
zip_out.add "public#{f.sub(dir.to_s,'')}", f
|
||||
end
|
||||
end
|
||||
files = @web.files_path
|
||||
Dir["#{files}/**/*"].each do |f|
|
||||
zip_out.add "files#{f.sub(files.to_s,'')}", f
|
||||
zip_out.put_next_entry "index.#{html_ext}"
|
||||
zip_out.puts "<html xmlns='http://www.w3.org/1999/xhtml'><head>" +
|
||||
"<META HTTP-EQUIV=\"Refresh\" CONTENT=\"0;URL=HomePage.#{file_type}\"></head></html>"
|
||||
end
|
||||
end
|
||||
FileUtils.rm_rf(Dir[@wiki.storage_path.join(file_prefix + '*.zip').to_s])
|
||||
FileUtils.rm_rf(Dir[File.join(@wiki.storage_path, file_prefix + '*.zip')])
|
||||
FileUtils.mv(tmp_path, file_path)
|
||||
send_file file_path
|
||||
end
|
||||
|
@ -506,7 +457,7 @@ EOL
|
|||
if params['rev']
|
||||
@revision_number = params['rev'].to_i
|
||||
else
|
||||
@revision_number = @page.rev_ids.size
|
||||
@revision_number = @page.revisions.length
|
||||
end
|
||||
@revision = @page.revisions[@revision_number - 1]
|
||||
end
|
||||
|
@ -552,7 +503,11 @@ EOL
|
|||
def rss_with_content_allowed?
|
||||
@web.password.nil? or @web.published?
|
||||
end
|
||||
|
||||
|
||||
def truncate(text, length = 30, truncate_string = '...')
|
||||
if text.length > length then text[0..(length - 3)] + truncate_string else text end
|
||||
end
|
||||
|
||||
def filter_spam(content)
|
||||
@@spam_patterns ||= load_spam_patterns
|
||||
@@spam_patterns.each do |pattern|
|
||||
|
@ -561,9 +516,9 @@ EOL
|
|||
end
|
||||
|
||||
def load_spam_patterns
|
||||
spam_patterns_file = Rails.root.join('config', 'spam_patterns.txt')
|
||||
spam_patterns_file = "#{RAILS_ROOT}/config/spam_patterns.txt"
|
||||
if File.exists?(spam_patterns_file)
|
||||
spam_patterns_file.readlines.inject([]) { |patterns, line| patterns << Regexp.new(line.chomp, Regexp::IGNORECASE) }
|
||||
File.readlines(spam_patterns_file).inject([]) { |patterns, line| patterns << Regexp.new(line.chomp, Regexp::IGNORECASE) }
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# The methods added to this helper will be available to all templates in the application.
|
||||
module ApplicationHelper
|
||||
require 'instiki_stringsupport'
|
||||
|
||||
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
|
||||
# where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and
|
||||
|
@ -31,7 +30,7 @@ require 'instiki_stringsupport'
|
|||
end
|
||||
end
|
||||
|
||||
html_options.join("\n").html_safe
|
||||
html_options.join("\n")
|
||||
end
|
||||
|
||||
# Creates a hyperlink to a Wiki page, without checking if the page exists or not
|
||||
|
@ -39,39 +38,39 @@ require 'instiki_stringsupport'
|
|||
link_to(
|
||||
text || page.plain_name,
|
||||
{:web => @web.address, :action => 'show', :id => page.name, :only_path => true},
|
||||
html_options).html_safe
|
||||
html_options)
|
||||
end
|
||||
|
||||
# Creates a hyperlink to a Wiki page, or to a "new page" form if the page doesn't exist yet
|
||||
def link_to_page(page_name, web = @web, text = nil, options = {})
|
||||
raise 'Web not defined' if web.nil?
|
||||
UrlGenerator.new(@controller).make_link(@web, page_name, web, text,
|
||||
options.merge(:base_url => "#{base_url}/#{web.address}")).html_safe
|
||||
UrlGenerator.new(@controller).make_link(page_name, web, text,
|
||||
options.merge(:base_url => "#{base_url}/#{web.address}"))
|
||||
end
|
||||
|
||||
def author_link(page, options = {})
|
||||
UrlGenerator.new(@controller).make_link(@web, page.author.name, page.web, nil, options).purify.html_safe
|
||||
UrlGenerator.new(@controller).make_link(page.author.name, page.web, nil, options)
|
||||
end
|
||||
|
||||
# Create a hyperlink to a particular revision of a Wiki page
|
||||
def link_to_revision(page, revision_number, text = nil, mode = nil, html_options = {})
|
||||
revision_number == page.rev_ids.size ?
|
||||
revision_number == page.revisions.length ?
|
||||
link_to(
|
||||
text || page.plain_name,
|
||||
{:web => @web.address, :action => 'show', :id => page.name,
|
||||
:mode => mode}, html_options).html_safe :
|
||||
:mode => mode}, html_options) :
|
||||
link_to(
|
||||
text || page.plain_name + "(rev # #{revision_number})".html_safe,
|
||||
text || page.plain_name + "(rev # #{revision_number})",
|
||||
{:web => @web.address, :action => 'revision', :id => page.name,
|
||||
:rev => revision_number, :mode => mode}, html_options).html_safe
|
||||
:rev => revision_number, :mode => mode}, html_options)
|
||||
end
|
||||
|
||||
# Create a hyperlink to the history of a particular Wiki page
|
||||
def link_to_history(page, text = nil, html_options = {})
|
||||
link_to(
|
||||
text || page.plain_name + "(history)".html_safe,
|
||||
text || page.plain_name + "(history)",
|
||||
{:web => @web.address, :action => 'history', :id => page.name},
|
||||
html_options).html_safe
|
||||
html_options)
|
||||
end
|
||||
|
||||
def base_url
|
||||
|
@ -84,19 +83,19 @@ require 'instiki_stringsupport'
|
|||
if @categories.empty?
|
||||
''
|
||||
else
|
||||
("<div id=\"categories\">\n" +
|
||||
"<div id=\"categories\">\n" +
|
||||
'<strong>Categories</strong>:' +
|
||||
'[' + link_to_unless_current('Any', :web => @web.address, :action => self.action_name, :category => nil) + "]\n" +
|
||||
@categories.map { |c|
|
||||
link_to_unless_current(c.html_safe, :web => @web.address, :action => self.action_name, :category => c)
|
||||
link_to_unless_current(c, :web => @web.address, :action => self.action_name, :category => c)
|
||||
}.join(', ') + "\n" +
|
||||
'</div>').html_safe
|
||||
'</div>'
|
||||
end
|
||||
end
|
||||
|
||||
# Performs HTML escaping on text, but keeps linefeeds intact (by replacing them with <br/>)
|
||||
def escape_preserving_linefeeds(text)
|
||||
h(text).gsub(/\n/, '<br/>').as_utf8.html_safe
|
||||
h(text).gsub(/\n/, '<br/>')
|
||||
end
|
||||
|
||||
def format_date(date, include_time = true)
|
||||
|
@ -109,22 +108,7 @@ require 'instiki_stringsupport'
|
|||
end
|
||||
|
||||
def rendered_content(page)
|
||||
PageRenderer.new(page.current_revision).display_content
|
||||
end
|
||||
|
||||
def truncate(text, *args)
|
||||
options = args.extract_options!
|
||||
options.reverse_merge!(:length => 30, :omission => "...")
|
||||
return text.html_safe if text.num_chars <= options[:length]
|
||||
len = options[:length] - options[:omission].as_utf8.num_chars
|
||||
t = ''
|
||||
text.split.collect do |word|
|
||||
if t.num_chars + word.num_chars <= len
|
||||
t << word + ' '
|
||||
else
|
||||
return (t.chop + options[:omission]).html_safe
|
||||
end
|
||||
end
|
||||
PageRenderer.new(page.revisions.last).display_content
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ module WikiHelper
|
|||
menu << back_for_revision if @revision_number > 1
|
||||
menu << current_revision
|
||||
menu << see_or_hide_changes_for_revision if @revision_number > 1
|
||||
menu << history if @page.rev_ids.size > 1
|
||||
menu << history if @page.revisions.length > 1
|
||||
menu << rollback
|
||||
menu
|
||||
end
|
||||
|
@ -15,11 +15,11 @@ module WikiHelper
|
|||
menu = []
|
||||
menu << edit_page
|
||||
menu << edit_web if @page.name == "HomePage"
|
||||
if @page.rev_ids.size > 1
|
||||
if @page.revisions.length > 1
|
||||
menu << back_for_page
|
||||
menu << see_or_hide_changes_for_page
|
||||
end
|
||||
menu << history if @page.rev_ids.size > 1
|
||||
menu << history if @page.revisions.length > 1
|
||||
menu
|
||||
end
|
||||
|
||||
|
@ -40,15 +40,15 @@ module WikiHelper
|
|||
end
|
||||
|
||||
def forward
|
||||
if @revision_number < @page.rev_ids.size - 1
|
||||
if @revision_number < @page.revisions.length - 1
|
||||
link_to('Forward in time',
|
||||
{:web => @web.address, :action => 'revision', :id => @page.name, :rev => @revision_number + 1},
|
||||
{:class => 'navlink', :accesskey => 'F', :id => 'to_next_revision', :rel => 'nofollow'}) +
|
||||
" <span class='revisions'>(#{@revision.page.rev_ids.size - @revision_number} more)</span> ".html_safe
|
||||
" <span class='revisions'>(#{@revision.page.revisions.length - @revision_number} more)</span> "
|
||||
else
|
||||
link_to('Forward in time', {:web => @web.address, :action => 'show', :id => @page.name},
|
||||
{:class => 'navlink', :accesskey => 'F', :id => 'to_next_revision', :rel => 'nofollow'}) +
|
||||
" <span class='revisions'>(to current)</span>".html_safe
|
||||
" <span class='revisions'>(to current)</span>"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -56,15 +56,15 @@ module WikiHelper
|
|||
link_to('Back in time',
|
||||
{:web => @web.address, :action => 'revision', :id => @page.name, :rev => @revision_number - 1},
|
||||
{:class => 'navlink', :id => 'to_previous_revision', :rel => 'nofollow'}) +
|
||||
" <span class='revisions'>(#{@revision_number - 1} more)</span>".html_safe
|
||||
" <span class='revisions'>(#{@revision_number - 1} more)</span>"
|
||||
end
|
||||
|
||||
def back_for_page
|
||||
link_to('Back in time',
|
||||
{:web => @web.address, :action => 'revision', :id => @page.name,
|
||||
:rev => @page.rev_ids.size - 1},
|
||||
:rev => @page.revisions.length - 1},
|
||||
{:class => 'navlink', :accesskey => 'B', :id => 'to_previous_revision', :rel => 'nofollow'}) +
|
||||
" <span class='revisions'>(#{@page.rev_ids.size - 1} #{@page.rev_ids.size - 1 == 1 ? 'revision' : 'revisions'})</span>".html_safe
|
||||
" <span class='revisions'>(#{@page.revisions.length - 1} #{@page.revisions.length - 1 == 1 ? 'revision' : 'revisions'})</span>"
|
||||
end
|
||||
|
||||
def current_revision
|
||||
|
@ -91,4 +91,6 @@ module WikiHelper
|
|||
{:class => 'navlink', :id => 'rollback', :rel => 'nofollow'})
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
# Allow the metal piece to run in isolation
|
||||
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)
|
||||
|
||||
require 'instiki_stringsupport'
|
||||
|
||||
class Itex
|
||||
def self.call(env)
|
||||
if env["PATH_INFO"] =~ /^\/itex/
|
||||
[200, {"Content-Type" => "application/xml"}, [response(env)]]
|
||||
else
|
||||
[404, {"Content-Type" => "text/html"}, ["Not Found"]]
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# plugable XML parser; falls back to REXML
|
||||
begin
|
||||
require 'nokogiri'
|
||||
def self.xmlparse(text)
|
||||
Nokogiri::XML(text) { |config| config.strict }
|
||||
end
|
||||
rescue LoadError
|
||||
require 'rexml/document'
|
||||
def self.xmlparse(text)
|
||||
REXML::Document.new(text)
|
||||
end
|
||||
end
|
||||
|
||||
#error message to return
|
||||
def self.error(str)
|
||||
"<math xmlns='http://www.w3.org/1998/Math/MathML' display='inline'><merror><mtext>" +
|
||||
str + "</mtext></merror></math>"
|
||||
end
|
||||
|
||||
# itex2MML parser
|
||||
begin
|
||||
require 'itextomml'
|
||||
def self.parse_itex(tex, filter)
|
||||
Itex2MML::Parser.new.send(filter, tex).to_utf8
|
||||
end
|
||||
rescue LoadError
|
||||
def self.parse_itex(tex, filter)
|
||||
error("Please install the itex2MML Ruby bindings.")
|
||||
end
|
||||
end
|
||||
|
||||
# the actual response
|
||||
def self.response(env)
|
||||
params = Rack::Request.new(env).params
|
||||
tex = (params['tex'] || '').purify.strip
|
||||
case params['display']
|
||||
when 'block'
|
||||
filter = :block_filter
|
||||
else
|
||||
filter = :inline_filter
|
||||
end
|
||||
return "<math xmlns='http://www.w3.org/1998/Math/MathML' display='" +
|
||||
filter.to_s[/(.*?)_filter/] + "'/>" if tex == ''
|
||||
begin
|
||||
doc = parse_itex(tex, filter)
|
||||
# make sure the result is well-formed, before sending it off
|
||||
begin
|
||||
xmlparse(doc)
|
||||
rescue
|
||||
return error("Ill-formed XML.")
|
||||
end
|
||||
return doc
|
||||
rescue Itex2MML::Error => e
|
||||
error(e.to_s)
|
||||
rescue
|
||||
error("Unknown Error")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -3,7 +3,7 @@ class Author < String
|
|||
attr_reader :name
|
||||
def initialize(name, ip = nil)
|
||||
@ip = ip
|
||||
super(name.as_utf8)
|
||||
super(name)
|
||||
end
|
||||
|
||||
def name=(value)
|
||||
|
|
|
@ -1,17 +1,11 @@
|
|||
class Page < ActiveRecord::Base
|
||||
belongs_to :web
|
||||
has_many :revisions, :order => 'id', :dependent => :destroy
|
||||
#In many cases, we don't need to instantiate the full revisions (with all that textual data)
|
||||
has_many :rev_ids, :order => 'id', :class_name => 'Revision', :select => 'id, revised_at, page_id, author, ip'
|
||||
has_many :wiki_references, :order => 'referenced_name'
|
||||
has_one :current_revision, :class_name => 'Revision', :order => 'id DESC'
|
||||
|
||||
def name
|
||||
read_attribute(:name).as_utf8
|
||||
end
|
||||
|
||||
def revise(content, name, time, author, renderer)
|
||||
revisions_size = new_record? ? 0 : rev_ids.size
|
||||
revisions_size = new_record? ? 0 : revisions.size
|
||||
if (revisions_size > 0) and content == current_revision.content and name == self.name
|
||||
raise Instiki::ValidationError.new(
|
||||
"You have tried to save page '#{name}' without changing its content")
|
||||
|
@ -48,11 +42,11 @@ class Page < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def revisions?
|
||||
rev_ids.size > 1
|
||||
revisions.size > 1
|
||||
end
|
||||
|
||||
def previous_revision(revision)
|
||||
revision_index = rev_ids.each_with_index do |rev, index|
|
||||
revision_index = revisions.each_with_index do |rev, index|
|
||||
if rev.id == revision.id
|
||||
break index
|
||||
else
|
||||
|
@ -74,10 +68,6 @@ class Page < ActiveRecord::Base
|
|||
wiki_references.select { |ref| ref.wiki_word? }.map { |ref| ref.referenced_name }
|
||||
end
|
||||
|
||||
def categories
|
||||
wiki_references.select { |ref| ref.category? }.map { |ref| ref.referenced_name }
|
||||
end
|
||||
|
||||
def linked_from
|
||||
web.select.pages_that_link_to(name)
|
||||
end
|
||||
|
@ -92,7 +82,7 @@ class Page < ActiveRecord::Base
|
|||
|
||||
# Returns the original wiki-word name as separate words, so "MyPage" becomes "My Page".
|
||||
def plain_name
|
||||
web.brackets_only? ? name.escapeHTML.html_safe : WikiWords.separate(name).escapeHTML.html_safe
|
||||
web.brackets_only? ? CGI.escapeHTML(name) : CGI.escapeHTML(WikiWords.separate(name))
|
||||
end
|
||||
|
||||
LOCKING_PERIOD = 30.minutes
|
||||
|
@ -114,7 +104,7 @@ class Page < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def to_param
|
||||
name.as_utf8
|
||||
name
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -83,17 +83,12 @@ class PageSet < Array
|
|||
# Returns all the wiki words in this page set for which
|
||||
# there are no pages in this page set's web
|
||||
def wanted_pages
|
||||
known_pages = (web.select.names + redirected_names).uniq
|
||||
wiki_words - known_pages
|
||||
wiki_words - web.select.names
|
||||
end
|
||||
|
||||
def names
|
||||
self.map { |page| page.name }
|
||||
end
|
||||
|
||||
def redirected_names
|
||||
self.wiki_words.select {|name| web.has_redirect_for?(name) }.uniq.sort
|
||||
end
|
||||
|
||||
def wiki_words
|
||||
self.inject([]) { |wiki_words, page|
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
class Revision < ActiveRecord::Base
|
||||
belongs_to :page
|
||||
composed_of :author, :mapping => [ %w(author name), %w(ip ip) ]
|
||||
|
||||
def content
|
||||
read_attribute(:content).as_utf8
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,75 +1,48 @@
|
|||
require 'instiki_stringsupport'
|
||||
|
||||
class Web < ActiveRecord::Base
|
||||
## Associations
|
||||
|
||||
has_many :pages, :dependent => :destroy
|
||||
has_many :pages, :dependent => :destroy
|
||||
has_many :wiki_files, :dependent => :destroy
|
||||
|
||||
has_many :revisions, :through => :pages
|
||||
|
||||
## Hooks
|
||||
|
||||
before_save :sanitize_markup
|
||||
after_save :create_files_directory
|
||||
|
||||
before_validation :validate_address
|
||||
|
||||
## Validations
|
||||
|
||||
validates_uniqueness_of :address, :message => 'already exists'
|
||||
validates_length_of :color, :in => 3..6
|
||||
|
||||
## Methods
|
||||
|
||||
# @return [Wiki] a new Wiki instance
|
||||
def wiki
|
||||
Wiki.new
|
||||
end
|
||||
|
||||
|
||||
def settings_changed?(markup, safe_mode, brackets_only)
|
||||
self.markup != markup ||
|
||||
self.safe_mode != safe_mode ||
|
||||
self.brackets_only != brackets_only
|
||||
end
|
||||
|
||||
|
||||
def add_page(name, content, time, author, renderer)
|
||||
page = page(name) || pages.build(:name => name)
|
||||
page = page(name) || Page.new(:web => self, :name => name)
|
||||
page.revise(content, name, time, author, renderer)
|
||||
end
|
||||
|
||||
# @return [Array<String>] a collection of all the names of the authors that
|
||||
# have ever contributed to the pages for this Web
|
||||
|
||||
def authors
|
||||
revisions.all(
|
||||
:select => "DISTINCT revisions.author",
|
||||
:order => "1"
|
||||
).collect(&:author)
|
||||
connection.select_all(
|
||||
'SELECT DISTINCT r.author AS author ' +
|
||||
'FROM revisions r ' +
|
||||
'JOIN pages p ON p.id = r.page_id ' +
|
||||
'WHERE p.web_id = ' + self.id.to_s +
|
||||
' ORDER by 1 '
|
||||
).collect { |row| row['author'] }
|
||||
end
|
||||
|
||||
def categories
|
||||
select.map { |page| page.categories }.flatten.uniq.sort
|
||||
end
|
||||
|
||||
# @param [String] name the name of some associated Page record to find
|
||||
# @return [Page, nil] the associated Page record, or +nil+ if no record is
|
||||
# found with the provided name
|
||||
def page(name)
|
||||
pages.find_by_name(name)
|
||||
pages.first(:conditions => ['name = ?', name])
|
||||
end
|
||||
|
||||
# @return [Page] the last associated Page record
|
||||
|
||||
def last_page
|
||||
pages.last
|
||||
return Page.first(:order => 'id desc', :conditions => ['web_id = ?', self.id])
|
||||
end
|
||||
|
||||
# @param [String] name the name of some potential Page record
|
||||
# @return [Boolean] whether or not a given Page record exists with a given
|
||||
# name
|
||||
def has_page?(name)
|
||||
pages.exists?(:name => name)
|
||||
Page.count(:conditions => ['web_id = ? AND name = ?', id, name]) > 0
|
||||
end
|
||||
|
||||
|
||||
def has_redirect_for?(name)
|
||||
WikiReference.page_that_redirects_for(self, name)
|
||||
end
|
||||
|
@ -79,11 +52,11 @@ class Web < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def has_file?(file_name)
|
||||
wiki_files.exists?(:file_name => file_name)
|
||||
WikiFile.find_by_file_name(file_name) != nil
|
||||
end
|
||||
|
||||
def file_list(sort_order="file_name")
|
||||
wiki_files.all(:order => sort_order)
|
||||
|
||||
def file_list(sort_order = 'file_name')
|
||||
WikiFile.all(:order => sort_order, :conditions => ['web_id = ?', id])
|
||||
end
|
||||
|
||||
def pages_that_link_to(page_name)
|
||||
|
@ -94,95 +67,82 @@ class Web < ActiveRecord::Base
|
|||
WikiReference.pages_that_link_to_file(self, file_name)
|
||||
end
|
||||
|
||||
# @param [String] file_name the name of some WikiFile of interest
|
||||
# @return [String, nil] the description of some WikiFile of interest, nil if
|
||||
# the WikiFile could not be found
|
||||
def description(file_name)
|
||||
wiki_files.find_by_file_name(file_name).try(:description)
|
||||
file = WikiFile.find_by_file_name(file_name)
|
||||
file.description if file
|
||||
end
|
||||
|
||||
# @return [Symbol] the type of markup used by this Web
|
||||
def markup
|
||||
self[:markup].to_sym
|
||||
read_attribute('markup').to_sym
|
||||
end
|
||||
|
||||
# @return [Hash] a Hash wherein the key is some author's name, and the
|
||||
# values are an array of page names for that author.
|
||||
def page_names_by_author
|
||||
data = revisions.all(
|
||||
:select => "DISTINCT revisions.author AS author, pages.name AS page_name",
|
||||
:order => "pages.name"
|
||||
)
|
||||
|
||||
data.inject({}) do |result, revision|
|
||||
result[revision.author] ||= []
|
||||
result[revision.author] << revision.page_name
|
||||
result
|
||||
end
|
||||
connection.select_all(
|
||||
'SELECT DISTINCT r.author AS author, p.name AS page_name ' +
|
||||
'FROM revisions r ' +
|
||||
'JOIN pages p ON r.page_id = p.id ' +
|
||||
"WHERE p.web_id = #{self.id} " +
|
||||
'ORDER by p.name'
|
||||
).inject({}) { |result, row|
|
||||
author, page_name = row['author'], row['page_name']
|
||||
result[author] = [] unless result.has_key?(author)
|
||||
result[author] << page_name
|
||||
result
|
||||
}
|
||||
end
|
||||
|
||||
# OPTIMIZE Use the +delete_all+ with conditions for extra efficiency
|
||||
def remove_pages(pages_to_be_removed)
|
||||
pages_to_be_removed.each { |p| p.destroy }
|
||||
end
|
||||
|
||||
|
||||
def revised_at
|
||||
select.most_recent_revision
|
||||
end
|
||||
|
||||
|
||||
def select(&condition)
|
||||
PageSet.new(self, pages, condition)
|
||||
end
|
||||
|
||||
|
||||
def select_all
|
||||
PageSet.new(self, pages, nil)
|
||||
end
|
||||
|
||||
# @return [String] uses the +address+ attribute for this record's parameter name
|
||||
|
||||
def to_param
|
||||
address
|
||||
end
|
||||
|
||||
# Called by an +after_save+ hook. Creates the directory that houses this
|
||||
# Web's associated files.
|
||||
#
|
||||
# TODO Move this into the WikiFile model
|
||||
|
||||
def create_files_directory
|
||||
return unless allow_uploads == 1
|
||||
|
||||
dummy_file = wiki_files.build(
|
||||
:file_name => "0",
|
||||
:description => "0",
|
||||
:content => "0"
|
||||
)
|
||||
|
||||
dummy_file = self.wiki_files.build(:file_name => '0', :description => '0', :content => '0')
|
||||
File.umask(0002)
|
||||
|
||||
dir = File.dirname(dummy_file.content_path)
|
||||
begin
|
||||
dummy_file.content_path.parent.mkpath
|
||||
require 'fileutils'
|
||||
FileUtils.mkdir_p dir
|
||||
dummy_file.save
|
||||
dummy_file.destroy
|
||||
rescue => e
|
||||
logger.error "Failed create files directory for #{address}: #{e}"
|
||||
logger.error("Failed create files directory for #{self.address}: #{e}")
|
||||
raise "Instiki could not create directory to store uploaded files. " +
|
||||
"Please make sure that Instiki is allowed to create directory " +
|
||||
"#{dummy_file.content_path.expand_path} and add files to it."
|
||||
"#{File.expand_path(dir)} and add files to it."
|
||||
end
|
||||
end
|
||||
|
||||
# @return [Pathname] the path to the files for this record
|
||||
|
||||
def files_path
|
||||
path = Rails.root.join("webs")
|
||||
if default_web?
|
||||
path.join("files")
|
||||
"#{RAILS_ROOT}/webs/files"
|
||||
else
|
||||
path.join(address, "files")
|
||||
"#{RAILS_ROOT}/webs/#{self.address}/files"
|
||||
end
|
||||
end
|
||||
|
||||
# @return [Pathname] the path to PNGs for this record
|
||||
def blahtex_pngs_path
|
||||
files_path.join("pngs")
|
||||
if default_web?
|
||||
"#{RAILS_ROOT}/webs/files/pngs"
|
||||
else
|
||||
"#{RAILS_ROOT}/webs/#{self.address}/files/pngs"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -191,31 +151,31 @@ class Web < ActiveRecord::Base
|
|||
def wiki_words
|
||||
pages.inject([]) { |wiki_words, page| wiki_words << page.wiki_words }.flatten.uniq
|
||||
end
|
||||
|
||||
|
||||
# Returns an array of all the page names on this web
|
||||
def page_names
|
||||
pages.map { |p| p.name }
|
||||
end
|
||||
|
||||
|
||||
protected
|
||||
|
||||
before_save :sanitize_markup
|
||||
after_save :create_files_directory
|
||||
before_validation :validate_address
|
||||
validates_uniqueness_of :address
|
||||
validates_length_of :color, :in => 3..6
|
||||
|
||||
def sanitize_markup
|
||||
self.markup = markup.to_s
|
||||
end
|
||||
|
||||
|
||||
def validate_address
|
||||
if ['create_system', 'create_web', 'delete_web', 'delete_files', 'web_list', ''].include?(address)
|
||||
self.errors.add(:address, 'is not a valid address')
|
||||
raise Instiki::ValidationError.new("\"#{address.purify.escapeHTML}\" #{errors.on(:address)}")
|
||||
end
|
||||
unless address == CGI.escape(address)
|
||||
self.errors.add(:address, 'should contain only valid URI characters')
|
||||
raise Instiki::ValidationError.new("#{self.class.human_attribute_name('address')} #{errors.on(:address)}")
|
||||
end
|
||||
end
|
||||
|
||||
# @return [Boolean] whether or not this record is considered the default Web
|
||||
|
||||
def default_web?
|
||||
defined?(DEFAULT_WEB) && address == DEFAULT_WEB
|
||||
defined? DEFAULT_WEB and self.address == DEFAULT_WEB
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
class Wiki
|
||||
|
||||
cattr_accessor :storage_path, :logger
|
||||
self.storage_path = Rails.root.join('storage')
|
||||
self.storage_path = "#{RAILS_ROOT}/storage/"
|
||||
|
||||
def authenticate(password)
|
||||
password == (system.password || 'instiki')
|
||||
|
@ -27,28 +27,14 @@ class Wiki
|
|||
if not (web = Web.find_by_address(old_address))
|
||||
raise Instiki::ValidationError.new("Web with address '#{old_address}' does not exist")
|
||||
end
|
||||
old_files_path = web.files_path
|
||||
|
||||
|
||||
web.update_attributes(:address => new_address, :name => name, :markup => markup, :color => color,
|
||||
:additional_style => additional_style, :safe_mode => safe_mode, :password => password, :published => published,
|
||||
:brackets_only => brackets_only, :count_pages => count_pages, :allow_uploads => allow_uploads, :max_upload_size => max_upload_size)
|
||||
@webs = nil
|
||||
raise Instiki::ValidationError.new("There is already a web with address '#{new_address}'") unless web.errors.on(:address).nil?
|
||||
web
|
||||
move_files(old_files_path, web.files_path)
|
||||
end
|
||||
|
||||
def move_files(old_path, new_path)
|
||||
return if new_path == old_path
|
||||
default_path = Rails.root.join("webs", "files")
|
||||
FileUtils.rmdir(new_path) if File.exist?(new_path)
|
||||
if [old_path, new_path].include? default_path
|
||||
File.rename(old_path, new_path)
|
||||
FileUtils.rmdir(old_path.parent) unless old_path == default_path
|
||||
else
|
||||
File.rename(old_path.parent, new_path.parent)
|
||||
end
|
||||
end
|
||||
|
||||
def read_page(web_address, page_name)
|
||||
ApplicationController.logger.debug "Reading page '#{page_name}' from web '#{web_address}'"
|
||||
|
|
|
@ -9,12 +9,13 @@ class WikiFile < ActiveRecord::Base
|
|||
validates_length_of :description, :maximum=>255
|
||||
|
||||
def self.find_by_file_name(file_name)
|
||||
first(:conditions => ['file_name = ?', file_name])
|
||||
find(:first, :conditions => ['file_name = ?', file_name])
|
||||
end
|
||||
|
||||
SANE_FILE_NAME = /^[a-zA-Z0-9\-_\. ]*$/
|
||||
def validate
|
||||
if file_name
|
||||
if ! WikiFile.is_valid?(file_name)
|
||||
if file_name !~ SANE_FILE_NAME
|
||||
errors.add("file_name", "is invalid. Only latin characters, digits, dots, underscores, " +
|
||||
"dashes and spaces are accepted")
|
||||
elsif file_name == '.' or file_name == '..'
|
||||
|
@ -45,7 +46,7 @@ class WikiFile < ActiveRecord::Base
|
|||
end
|
||||
|
||||
def content_path
|
||||
web.files_path.join(file_name)
|
||||
web.files_path + '/' + file_name
|
||||
end
|
||||
|
||||
def write_content_to_file
|
||||
|
@ -57,10 +58,7 @@ class WikiFile < ActiveRecord::Base
|
|||
require 'fileutils'
|
||||
FileUtils.rm_f(content_path) if File.exists?(content_path)
|
||||
end
|
||||
|
||||
SANE_FILE_NAME = /^[a-zA-Z0-9\-_\. ]*$/
|
||||
def self.is_valid?(name)
|
||||
name =~ SANE_FILE_NAME
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
|
|
@ -12,10 +12,6 @@ class WikiReference < ActiveRecord::Base
|
|||
belongs_to :page
|
||||
validates_inclusion_of :link_type, :in => [LINKED_PAGE, WANTED_PAGE, REDIRECTED_PAGE, INCLUDED_PAGE, CATEGORY, AUTHOR, FILE, WANTED_FILE]
|
||||
|
||||
def referenced_name
|
||||
read_attribute(:referenced_name).as_utf8
|
||||
end
|
||||
|
||||
def self.link_type(web, page_name)
|
||||
if web.has_page?(page_name) || self.page_that_redirects_for(web, page_name)
|
||||
LINKED_PAGE
|
||||
|
@ -46,7 +42,7 @@ class WikiReference < ActiveRecord::Base
|
|||
query = 'SELECT name FROM pages JOIN wiki_references ' +
|
||||
'ON pages.id = wiki_references.page_id ' +
|
||||
'WHERE wiki_references.referenced_name = ? ' +
|
||||
"AND wiki_references.link_type in ('#{FILE}','#{WANTED_FILE}') " +
|
||||
"AND wiki_references.link_type in ('#{FILE}') " +
|
||||
"AND pages.web_id = '#{web.id}'"
|
||||
names = connection.select_all(sanitize_sql([query, file_name])).map { |row| row['name'] }
|
||||
end
|
||||
|
@ -78,9 +74,9 @@ class WikiReference < ActiveRecord::Base
|
|||
"AND wiki_references.link_type = '#{REDIRECTED_PAGE}' " +
|
||||
"AND pages.web_id = '#{web.id}'"
|
||||
row = connection.select_one(sanitize_sql([query, page_name]))
|
||||
row['name'].as_utf8 if row
|
||||
row['name'] if row
|
||||
end
|
||||
|
||||
|
||||
def self.pages_in_category(web, category)
|
||||
query =
|
||||
"SELECT name FROM pages JOIN wiki_references " +
|
||||
|
@ -88,7 +84,7 @@ class WikiReference < ActiveRecord::Base
|
|||
"WHERE wiki_references.referenced_name = ? " +
|
||||
"AND wiki_references.link_type = '#{CATEGORY}' " +
|
||||
"AND pages.web_id = '#{web.id}'"
|
||||
names = connection.select_all(sanitize_sql([query, category])).map { |row| row['name'].as_utf8 }
|
||||
names = connection.select_all(sanitize_sql([query, category])).map { |row| row['name'] }
|
||||
end
|
||||
|
||||
def self.list_categories(web)
|
||||
|
@ -97,7 +93,7 @@ class WikiReference < ActiveRecord::Base
|
|||
"ON wiki_references.page_id = pages.id " +
|
||||
"WHERE wiki_references.link_type = '#{CATEGORY}' " +
|
||||
"AND pages.web_id = '#{web.id}'"
|
||||
connection.select_all(query).map { |row| row['referenced_name'].as_utf8 }
|
||||
connection.select_all(query).map { |row| row['referenced_name'] }
|
||||
end
|
||||
|
||||
def wiki_word?
|
||||
|
@ -132,8 +128,4 @@ class WikiReference < ActiveRecord::Base
|
|||
link_type == WANTED_FILE
|
||||
end
|
||||
|
||||
def category?
|
||||
link_type == CATEGORY
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
onchange="proposeAddress();" />   
|
||||
<label for="address">Address:</label> <input type="text" class="disableAutoComplete" id="address" name="address" value="<%= @web.address %>"
|
||||
onchange="cleanAddress();" />
|
||||
<em>(Letters and digits only)</em>
|
||||
<small><em>(Letters and digits only)</em></small>
|
||||
</div>
|
||||
|
||||
<h2 style="margin-bottom: 3px">Specialize</h2>
|
||||
|
@ -46,18 +46,18 @@
|
|||
}, @web.color) %>
|
||||
</select>
|
||||
<p>
|
||||
<input type="checkbox" class="disableAutoComplete" id="safe_mode" name="safe_mode" <%= raw 'checked="checked"' if @web.safe_mode? %> />
|
||||
<input type="checkbox" class="disableAutoComplete" id="safe_mode" name="safe_mode" <%= 'checked="checked"' if @web.safe_mode? %> />
|
||||
<label for="safe_mode">Safe mode
|
||||
<em>- strip HTML tags and stylesheet options from the content of all pages</em></label>
|
||||
<br/>
|
||||
<input type="checkbox" class="disableAutoComplete" id="brackets_only" name="brackets_only" <%= raw 'checked="checked"' if @web.brackets_only? %> />
|
||||
<input type="checkbox" class="disableAutoComplete" id="brackets_only" name="brackets_only" <%= 'checked="checked"' if @web.brackets_only? %> />
|
||||
<label for="brackets_only">Brackets only
|
||||
<em>- require all wiki words to be as [[wiki word]], WikiWord links won't be created</em></label>
|
||||
<br/>
|
||||
<input type="checkbox" class="disableAutoComplete" id="count_pages" name="count_pages" <%= raw 'checked="checked"' if @web.count_pages? %> />
|
||||
<input type="checkbox" class="disableAutoComplete" id="count_pages" name="count_pages" <%= 'checked="checked"' if @web.count_pages? %> />
|
||||
<label for="count_pages">Count pages</label>
|
||||
<br/>
|
||||
<input type="checkbox" class="disableAutoComplete" name="allow_uploads" <%= raw 'checked="checked"' if @web.allow_uploads? %> />
|
||||
<input type="checkbox" class="disableAutoComplete" name="allow_uploads" <%= 'checked="checked"' if @web.allow_uploads? %> />
|
||||
Allow uploads of no more than
|
||||
<input type="text" class="disableAutoComplete" name="max_upload_size" value="<%= @web.max_upload_size %>"
|
||||
size="20" />
|
||||
|
@ -71,8 +71,7 @@
|
|||
Stylesheet tweaks >></a>
|
||||
<em>
|
||||
- add or change styles used by this web; styles defined here take precedence over
|
||||
instiki.css.<br/>
|
||||
Hint: View HTML source of a page you want to style to find ID names on individual
|
||||
instiki.css. Hint: View HTML source of a page you want to style to find ID names on individual
|
||||
tags.</em>
|
||||
<br/>
|
||||
<textarea id="additionalStyle" class="disableAutoComplete" cols="50" rows="20"
|
||||
|
@ -97,10 +96,10 @@
|
|||
<div class="help">
|
||||
You can turn on a read-only version of this web that's accessible even when the regular web
|
||||
is password protected.
|
||||
The published version is accessible through URLs like /<%= @web.address %>/published/HomePage.
|
||||
The published version is accessible through URLs like /wiki/published/HomePage.
|
||||
</div>
|
||||
<div class="inputBox">
|
||||
<input type="checkbox" id="published" name="published" class="disableAutoComplete" <%= raw 'checked="checked"' if @web.published? %> />
|
||||
<input type="checkbox" id="published" name="published" class="disableAutoComplete" <%= 'checked="checked"' if @web.published? %> />
|
||||
<label for="published">Publish this web</label>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<%-
|
||||
@title = "Delete #{@file_name}".html_safe
|
||||
@title = "Delete #{@file_name}"
|
||||
@hide_navigation = true
|
||||
-%>
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<input type="text" name="author" id="authorName" value="<%= @author %>"
|
||||
onclick="this.value == 'AnonymousCoward' ? this.value = '' : true" />
|
||||
<%- if @page -%>
|
||||
| <%= link_to 'Cancel', :web => @web.address, :action => 'file'%> <em>(unlocks page)</em>
|
||||
| <%= link_to 'Cancel', :web => @web.address, :action => 'file'%> <small>(unlocks page)</small>
|
||||
<%- end -%>
|
||||
|
||||
</p>
|
||||
|
|
|
@ -16,42 +16,19 @@
|
|||
|
||||
<%= javascript_include_tag 'page_helper' %>
|
||||
|
||||
<%= stylesheet_link_tag 'instiki', :media => 'all' unless @inline_style %>
|
||||
<%= stylesheet_link_tag 'syntax', :media => 'all' unless @inline_style %>
|
||||
<style type="text/css">
|
||||
h1#pageName, div.info, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, [actiontype="toggle"]:hover, #TextileHelp h3 {
|
||||
h1#pageName, div.info, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, #TextileHelp h3 {
|
||||
color: #<%= @web ? @web.color : "393" %>;
|
||||
}
|
||||
a:visited.existingWikiWord {
|
||||
color: #<%= darken(@web ? @web.color : "393") %>;
|
||||
}
|
||||
<%= Rails.root.join('public', 'stylesheets', 'instiki.css').read if @inline_style %>
|
||||
<%= Rails.root.join('public', 'stylesheets', 'syntax.css').read if @inline_style %>
|
||||
<%= File.read(RAILS_ROOT + '/public/stylesheets/instiki.css') if @inline_style %>
|
||||
</style>
|
||||
<%= "<style type='text/css'>#{@style_additions}</style>".html_safe if @style_additions %>
|
||||
<%= stylesheet_link_tag 'instiki' unless @inline_style %>
|
||||
<%= "<style type='text/css'>#{@style_additions}</style>" if @style_additions %>
|
||||
<style type="text/css"><!--/*--><![CDATA[/*><!--*/
|
||||
<%= @web && @web.additional_style ? @web.additional_style.html_safe : '' %>
|
||||
<%= @web ? @web.additional_style : '' %>
|
||||
/*]]>*/--></style>
|
||||
<%= javascript_include_tag :defaults %>
|
||||
<%= csrf_meta_tag %>
|
||||
<%- if @web -%>
|
||||
<%- if @web.markup == :markdownMML -%>
|
||||
<script type="text/x-mathjax-config">
|
||||
MathJax.Hub.Config({
|
||||
MathML: { useMathMLspacing: true },
|
||||
"HTML-CSS": { scale: 90 }
|
||||
});
|
||||
if (window._onload_fired_) MathJax.Hub.Startup.onload();
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
if (!(Prototype.Browser.Gecko || navigator.userAgent.match(/MathPlayer/))) {
|
||||
var s = document.createElement('script');
|
||||
s.src = "<%= compute_public_path('MathJax.js', 'MathJax').split('?')[0] %>?config=MML_HTMLorMML";
|
||||
document.querySelector('head').appendChild(s);
|
||||
window.addEventListener("load", function(){window._onload_fired_ = true} , false);
|
||||
};
|
||||
</script>
|
||||
<%- end -%>
|
||||
<%= auto_discovery_link_tag(:atom, {:controller => 'wiki', :web => @web.address, :action => 'atom_with_headlines'},
|
||||
:title => 'Atom with headlines') %>
|
||||
<%= auto_discovery_link_tag(:atom, {:controller => 'wiki', :web => @web.address, :action => 'atom_with_content'},
|
||||
|
@ -64,7 +41,7 @@
|
|||
<div id="Container">
|
||||
<div id="Content">
|
||||
<h1 id="pageName">
|
||||
<%= render(:file => 'svg_logo') if xhtml_enabled? %>
|
||||
<%= render(:file => 'svg_logo') if @controller.xhtml_enabled? %>
|
||||
<%- if @page and (@page.name == 'HomePage') and %w( show published print ).include?(@action_name) -%>
|
||||
<%= h(@web.name) + (@show_diff ? ' (changes)' : '') %>
|
||||
<%- elsif @web -%>
|
||||
|
|
|
@ -12,10 +12,10 @@
|
|||
h1#pageName, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, #TextileHelp h3 {
|
||||
color: #<%= @web ? @web.color : "393" %>;
|
||||
}
|
||||
<%= Rails.root.join('public', 'stylesheets', 'instiki.css').read if @inline_style %>
|
||||
<%= File.read(RAILS_ROOT + '/public/stylesheets/instiki.css') if @inline_style %>
|
||||
</style>
|
||||
|
||||
<%= stylesheet_link_tag 'instiki', :media => 'all' unless @inline_style %>
|
||||
<%= stylesheet_link_tag 'instiki' unless @inline_style %>
|
||||
|
||||
<style type="text/css"><!--/*--><![CDATA[/*><!--*/
|
||||
<%= @style_additions %>
|
||||
|
@ -31,11 +31,7 @@
|
|||
</h1>
|
||||
|
||||
<div id="Error-Content">
|
||||
<%= if :raw
|
||||
@content_for_layout
|
||||
else
|
||||
h @content_for_layout
|
||||
end %>
|
||||
<%= h @content_for_layout %>
|
||||
|
||||
</div> <!-- Error-Content -->
|
||||
|
||||
|
|
|
@ -1,265 +1 @@
|
|||
\documentclass[12pt,titlepage]{article}
|
||||
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amsfonts}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{amsthm}
|
||||
\usepackage{mathtools}
|
||||
<%- if @tex_content =~ /\\mathbb\{[^}]*[a-z0-9]+[^}]*\}/ -%>
|
||||
\usepackage{mathbbol}
|
||||
<% end -%>
|
||||
\usepackage{graphicx}
|
||||
\usepackage{color}
|
||||
\usepackage{ucs}
|
||||
\usepackage[utf8x]{inputenc}
|
||||
\usepackage{xparse}
|
||||
\usepackage{hyperref}
|
||||
|
||||
%----Macros----------
|
||||
%
|
||||
% Unresolved issues:
|
||||
%
|
||||
% \righttoleftarrow
|
||||
% \lefttorightarrow
|
||||
%
|
||||
% \color{} with HTML colorspec
|
||||
% \bgcolor
|
||||
% \array with options (without options, it's equivalent to the matrix environment)
|
||||
|
||||
% Of the standard HTML named colors, white, black, red, green, blue and yellow
|
||||
% are predefined in the color package. Here are the rest.
|
||||
\definecolor{aqua}{rgb}{0, 1.0, 1.0}
|
||||
\definecolor{fuschia}{rgb}{1.0, 0, 1.0}
|
||||
\definecolor{gray}{rgb}{0.502, 0.502, 0.502}
|
||||
\definecolor{lime}{rgb}{0, 1.0, 0}
|
||||
\definecolor{maroon}{rgb}{0.502, 0, 0}
|
||||
\definecolor{navy}{rgb}{0, 0, 0.502}
|
||||
\definecolor{olive}{rgb}{0.502, 0.502, 0}
|
||||
\definecolor{purple}{rgb}{0.502, 0, 0.502}
|
||||
\definecolor{silver}{rgb}{0.753, 0.753, 0.753}
|
||||
\definecolor{teal}{rgb}{0, 0.502, 0.502}
|
||||
|
||||
% Because of conflicts, \space and \mathop are converted to
|
||||
% \itexspace and \operatorname during preprocessing.
|
||||
|
||||
% itex: \space{ht}{dp}{wd}
|
||||
%
|
||||
% Height and baseline depth measurements are in units of tenths of an ex while
|
||||
% the width is measured in tenths of an em.
|
||||
\makeatletter
|
||||
\newdimen\itex@wd%
|
||||
\newdimen\itex@dp%
|
||||
\newdimen\itex@thd%
|
||||
\def\itexspace#1#2#3{\itex@wd=#3em%
|
||||
\itex@wd=0.1\itex@wd%
|
||||
\itex@dp=#2ex%
|
||||
\itex@dp=0.1\itex@dp%
|
||||
\itex@thd=#1ex%
|
||||
\itex@thd=0.1\itex@thd%
|
||||
\advance\itex@thd\the\itex@dp%
|
||||
\makebox[\the\itex@wd]{\rule[-\the\itex@dp]{0cm}{\the\itex@thd}}}
|
||||
\makeatother
|
||||
|
||||
% \tensor and \multiscript
|
||||
\makeatletter
|
||||
\newif\if@sup
|
||||
\newtoks\@sups
|
||||
\def\append@sup#1{\edef\act{\noexpand\@sups={\the\@sups #1}}\act}%
|
||||
\def\reset@sup{\@supfalse\@sups={}}%
|
||||
\def\mk@scripts#1#2{\if #2/ \if@sup ^{\the\@sups}\fi \else%
|
||||
\ifx #1_ \if@sup ^{\the\@sups}\reset@sup \fi {}_{#2}%
|
||||
\else \append@sup#2 \@suptrue \fi%
|
||||
\expandafter\mk@scripts\fi}
|
||||
\def\tensor#1#2{\reset@sup#1\mk@scripts#2_/}
|
||||
\def\multiscripts#1#2#3{\reset@sup{}\mk@scripts#1_/#2%
|
||||
\reset@sup\mk@scripts#3_/}
|
||||
\makeatother
|
||||
|
||||
% \slash
|
||||
\makeatletter
|
||||
\newbox\slashbox \setbox\slashbox=\hbox{$/$}
|
||||
\def\itex@pslash#1{\setbox\@tempboxa=\hbox{$#1$}
|
||||
\@tempdima=0.5\wd\slashbox \advance\@tempdima 0.5\wd\@tempboxa
|
||||
\copy\slashbox \kern-\@tempdima \box\@tempboxa}
|
||||
\def\slash{\protect\itex@pslash}
|
||||
\makeatother
|
||||
|
||||
% math-mode versions of \rlap, etc
|
||||
% from Alexander Perlis, "A complement to \smash, \llap, and lap"
|
||||
% http://math.arizona.edu/~aprl/publications/mathclap/
|
||||
\def\clap#1{\hbox to 0pt{\hss#1\hss}}
|
||||
\def\mathllap{\mathpalette\mathllapinternal}
|
||||
\def\mathrlap{\mathpalette\mathrlapinternal}
|
||||
\def\mathclap{\mathpalette\mathclapinternal}
|
||||
\def\mathllapinternal#1#2{\llap{$\mathsurround=0pt#1{#2}$}}
|
||||
\def\mathrlapinternal#1#2{\rlap{$\mathsurround=0pt#1{#2}$}}
|
||||
\def\mathclapinternal#1#2{\clap{$\mathsurround=0pt#1{#2}$}}
|
||||
|
||||
% Renames \sqrt as \oldsqrt and redefine root to result in \sqrt[#1]{#2}
|
||||
\let\oldroot\root
|
||||
\def\root#1#2{\oldroot #1 \of{#2}}
|
||||
\renewcommand{\sqrt}[2][]{\oldroot #1 \of{#2}}
|
||||
|
||||
% Manually declare the txfonts symbolsC font
|
||||
\DeclareSymbolFont{symbolsC}{U}{txsyc}{m}{n}
|
||||
\SetSymbolFont{symbolsC}{bold}{U}{txsyc}{bx}{n}
|
||||
\DeclareFontSubstitution{U}{txsyc}{m}{n}
|
||||
|
||||
% Manually declare the stmaryrd font
|
||||
\DeclareSymbolFont{stmry}{U}{stmry}{m}{n}
|
||||
\SetSymbolFont{stmry}{bold}{U}{stmry}{b}{n}
|
||||
|
||||
% Declare specific arrows from txfonts without loading the full package
|
||||
\makeatletter
|
||||
\def\re@DeclareMathSymbol#1#2#3#4{%
|
||||
\let#1=\undefined
|
||||
\DeclareMathSymbol{#1}{#2}{#3}{#4}}
|
||||
\re@DeclareMathSymbol{\neArrow}{\mathrel}{symbolsC}{116}
|
||||
\re@DeclareMathSymbol{\neArr}{\mathrel}{symbolsC}{116}
|
||||
\re@DeclareMathSymbol{\seArrow}{\mathrel}{symbolsC}{117}
|
||||
\re@DeclareMathSymbol{\seArr}{\mathrel}{symbolsC}{117}
|
||||
\re@DeclareMathSymbol{\nwArrow}{\mathrel}{symbolsC}{118}
|
||||
\re@DeclareMathSymbol{\nwArr}{\mathrel}{symbolsC}{118}
|
||||
\re@DeclareMathSymbol{\swArrow}{\mathrel}{symbolsC}{119}
|
||||
\re@DeclareMathSymbol{\swArr}{\mathrel}{symbolsC}{119}
|
||||
\re@DeclareMathSymbol{\nequiv}{\mathrel}{symbolsC}{46}
|
||||
\re@DeclareMathSymbol{\Perp}{\mathrel}{symbolsC}{121}
|
||||
\re@DeclareMathSymbol{\Vbar}{\mathrel}{symbolsC}{121}
|
||||
\re@DeclareMathSymbol{\sslash}{\mathrel}{stmry}{12}
|
||||
\re@DeclareMathSymbol{\invamp}{\mathrel}{symbolsC}{77}
|
||||
\re@DeclareMathSymbol{\parr}{\mathrel}{symbolsC}{77}
|
||||
\makeatother
|
||||
|
||||
% Widecheck
|
||||
\makeatletter
|
||||
\DeclareRobustCommand\widecheck[1]{{\mathpalette\@widecheck{#1}}}
|
||||
\def\@widecheck#1#2{%
|
||||
\setbox\z@\hbox{\m@th$#1#2$}%
|
||||
\setbox\tw@\hbox{\m@th$#1%
|
||||
\widehat{%
|
||||
\vrule\@width\z@\@height\ht\z@
|
||||
\vrule\@height\z@\@width\wd\z@}$}%
|
||||
\dp\tw@-\ht\z@
|
||||
\@tempdima\ht\z@ \advance\@tempdima2\ht\tw@ \divide\@tempdima\thr@@
|
||||
\setbox\tw@\hbox{%
|
||||
\raise\@tempdima\hbox{\scalebox{1}[-1]{\lower\@tempdima\box
|
||||
\tw@}}}%
|
||||
{\ooalign{\box\tw@ \cr \box\z@}}}
|
||||
\makeatother
|
||||
|
||||
% \mathraisebox{voffset}[height][depth]{something}
|
||||
\makeatletter
|
||||
\NewDocumentCommand\mathraisebox{moom}{%
|
||||
\IfNoValueTF{#2}{\def\@temp##1##2{\raisebox{#1}{$\m@th##1##2$}}}{%
|
||||
\IfNoValueTF{#3}{\def\@temp##1##2{\raisebox{#1}[#2]{$\m@th##1##2$}}%
|
||||
}{\def\@temp##1##2{\raisebox{#1}[#2][#3]{$\m@th##1##2$}}}}%
|
||||
\mathpalette\@temp{#4}}
|
||||
\makeatletter
|
||||
|
||||
% udots (taken from yhmath)
|
||||
\makeatletter
|
||||
\def\udots{\mathinner{\mkern2mu\raise\p@\hbox{.}
|
||||
\mkern2mu\raise4\p@\hbox{.}\mkern1mu
|
||||
\raise7\p@\vbox{\kern7\p@\hbox{.}}\mkern1mu}}
|
||||
\makeatother
|
||||
|
||||
%% Fix array
|
||||
\newcommand{\itexarray}[1]{\begin{matrix}#1\end{matrix}}
|
||||
%% \itexnum is a noop
|
||||
\newcommand{\itexnum}[1]{#1}
|
||||
|
||||
%% Renaming existing commands
|
||||
\newcommand{\underoverset}[3]{\underset{#1}{\overset{#2}{#3}}}
|
||||
\newcommand{\widevec}{\overrightarrow}
|
||||
\newcommand{\darr}{\downarrow}
|
||||
\newcommand{\nearr}{\nearrow}
|
||||
\newcommand{\nwarr}{\nwarrow}
|
||||
\newcommand{\searr}{\searrow}
|
||||
\newcommand{\swarr}{\swarrow}
|
||||
\newcommand{\curvearrowbotright}{\curvearrowright}
|
||||
\newcommand{\uparr}{\uparrow}
|
||||
\newcommand{\downuparrow}{\updownarrow}
|
||||
\newcommand{\duparr}{\updownarrow}
|
||||
\newcommand{\updarr}{\updownarrow}
|
||||
\newcommand{\gt}{>}
|
||||
\newcommand{\lt}{<}
|
||||
\newcommand{\map}{\mapsto}
|
||||
\newcommand{\embedsin}{\hookrightarrow}
|
||||
\newcommand{\Alpha}{A}
|
||||
\newcommand{\Beta}{B}
|
||||
\newcommand{\Zeta}{Z}
|
||||
\newcommand{\Eta}{H}
|
||||
\newcommand{\Iota}{I}
|
||||
\newcommand{\Kappa}{K}
|
||||
\newcommand{\Mu}{M}
|
||||
\newcommand{\Nu}{N}
|
||||
\newcommand{\Rho}{P}
|
||||
\newcommand{\Tau}{T}
|
||||
\newcommand{\Upsi}{\Upsilon}
|
||||
\newcommand{\omicron}{o}
|
||||
\newcommand{\lang}{\langle}
|
||||
\newcommand{\rang}{\rangle}
|
||||
\newcommand{\Union}{\bigcup}
|
||||
\newcommand{\Intersection}{\bigcap}
|
||||
\newcommand{\Oplus}{\bigoplus}
|
||||
\newcommand{\Otimes}{\bigotimes}
|
||||
\newcommand{\Wedge}{\bigwedge}
|
||||
\newcommand{\Vee}{\bigvee}
|
||||
\newcommand{\coproduct}{\coprod}
|
||||
\newcommand{\product}{\prod}
|
||||
\newcommand{\closure}{\overline}
|
||||
\newcommand{\integral}{\int}
|
||||
\newcommand{\doubleintegral}{\iint}
|
||||
\newcommand{\tripleintegral}{\iiint}
|
||||
\newcommand{\quadrupleintegral}{\iiiint}
|
||||
\newcommand{\conint}{\oint}
|
||||
\newcommand{\contourintegral}{\oint}
|
||||
\newcommand{\infinity}{\infty}
|
||||
\newcommand{\bottom}{\bot}
|
||||
\newcommand{\minusb}{\boxminus}
|
||||
\newcommand{\plusb}{\boxplus}
|
||||
\newcommand{\timesb}{\boxtimes}
|
||||
\newcommand{\intersection}{\cap}
|
||||
\newcommand{\union}{\cup}
|
||||
\newcommand{\Del}{\nabla}
|
||||
\newcommand{\odash}{\circleddash}
|
||||
\newcommand{\negspace}{\!}
|
||||
\newcommand{\widebar}{\overline}
|
||||
\newcommand{\textsize}{\normalsize}
|
||||
\renewcommand{\scriptsize}{\scriptstyle}
|
||||
\newcommand{\scriptscriptsize}{\scriptscriptstyle}
|
||||
\newcommand{\mathfr}{\mathfrak}
|
||||
\newcommand{\statusline}[2]{#2}
|
||||
\newcommand{\tooltip}[2]{#2}
|
||||
\newcommand{\toggle}[2]{#2}
|
||||
|
||||
% Theorem Environments
|
||||
\theoremstyle{plain}
|
||||
\newtheorem{theorem}{Theorem}
|
||||
\newtheorem{lemma}{Lemma}
|
||||
\newtheorem{prop}{Proposition}
|
||||
\newtheorem{cor}{Corollary}
|
||||
\newtheorem*{utheorem}{Theorem}
|
||||
\newtheorem*{ulemma}{Lemma}
|
||||
\newtheorem*{uprop}{Proposition}
|
||||
\newtheorem*{ucor}{Corollary}
|
||||
\theoremstyle{definition}
|
||||
\newtheorem{defn}{Definition}
|
||||
\newtheorem{example}{Example}
|
||||
\newtheorem*{udefn}{Definition}
|
||||
\newtheorem*{uexample}{Example}
|
||||
\theoremstyle{remark}
|
||||
\newtheorem{remark}{Remark}
|
||||
\newtheorem{note}{Note}
|
||||
\newtheorem*{uremark}{Remark}
|
||||
\newtheorem*{unote}{Note}
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\begin{document}
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
<%= @content_for_layout %>
|
||||
|
||||
\end{document}
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
<h3>Markdown+itex2MML formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">basics</a>, <a target="_new" href="http://michelf.com/projects/php-markdown/extra/">extended syntax</a>, <a target="_new" href="http://maruku.rubyforge.org/proposal.html">metadata</a>, <a target="_new" href="http://golem.ph.utexas.edu/instiki/show/Theorems">theorems</a>)</h3>
|
||||
<h3>Markdown+itex2MML formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">basics</a>, <a target="_new" href="http://maruku.rubyforge.org/maruku.html#extra">extended syntax</a>, <a target="_new" href="http://maruku.rubyforge.org/proposal.html">metadata</a>, <a target="_new" href="http://golem.ph.utexas.edu/instiki/show/Theorems">theorems</a>)</h3>
|
||||
<p>For a complete list of LaTeX commands supported here, see the <a href="http://golem.ph.utexas.edu/~distler/blog/itex2MMLcommands.html">itex2MML Commands Summary</a>.</p>
|
||||
<%= render(:file => "markdown_table") -%>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>**your text**</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>`my code`</td><td class="arrow">→</td><td><code>my code</code></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td>1. Numbered list<br />1. Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>Definition list<br />: is useful</td><td class="arrow">→</td><td><dl style="display:inline"><dt>Definition list</dt><dd>is useful</dd></dl></td></tr>
|
||||
<tr><td>[link name](URL)</td><td class="arrow">→</td><td><a href="URL">link name</a></td></tr>
|
||||
<tr><td>![Alt text](URL)</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
<tr><td>## Header ##<br />### Subheader ###<br />#### Etc. ####</td><td class="arrow">→</td><td><b><span style="font-size:1.2em">Header</span><br /><span style="font-size:1.1em">Subheader</span><br /><span style="font-size:1em">Etc.</span></b></td></tr>
|
||||
<tr><td>***</td><td class="arrow">→</td><td>Horizontal ruler</td></tr>
|
||||
<tr><td><http://url><br /><email@add.com></td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
</table>
|
||||
|
|
|
@ -1,3 +1,16 @@
|
|||
<h3>Markdown+blahtex/PNG formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">basics</a>, <a target="_new" href="http://michelf.com/projects/php-markdown/extra/">extended syntax</a>, <a target="_new" href="http://maruku.rubyforge.org/proposal.html">metadata</a>)</h3>
|
||||
<p>For a list of the LaTeX commands supported here, see the <a href="http://www.blahtex.org/manual.html">BlahTeX manual</a>.</p>
|
||||
<%= render(:file => "markdown_table") -%>
|
||||
<h3>Markdown+blahtex/PNG formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">basics</a>, <a target="_new" href="http://maruku.rubyforge.org/#extra">extended syntax</a>, <a target="_new" href="http://maruku.rubyforge.org/proposal.html">metadata</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>**your text**</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>`my code`</td><td class="arrow">→</td><td><code>my code</code></td></tr>
|
||||
<tr><td>$LaTeX code$</td><td class="arrow">→</td><td>Insert an inline <br /> math expression</td></tr>
|
||||
<tr><td>\[LaTeX code\]</td><td class="arrow">→</td><td>Insert a math <br /> expression on <br /> its own line</td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td>1. Numbered list<br />1. Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>Definition list<br />: is useful</td><td class="arrow">→</td><td><dl style="display:inline"><dt>Definition list</dt><dd>is useful</dd></dl></td></tr>
|
||||
<tr><td>[link name](URL)</td><td class="arrow">→</td><td><a href="URL">link name</a></td></tr>
|
||||
<tr><td>![Alt text](URL)</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
<tr><td>## Header ##<br />### Subheader ###<br />#### Etc. ####</td><td class="arrow">→</td><td><b><span style="font-size:1.2em">Header</span><br /><span style="font-size:1.1em">Subheader</span><br /><span style="font-size:1em">Etc.</span></b></td></tr>
|
||||
<tr><td>***</td><td class="arrow">→</td><td>Horizontal ruler</td></tr>
|
||||
<tr><td><http://url><br /><email@add.com></td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
</table>
|
||||
|
|
|
@ -1,2 +1,14 @@
|
|||
<h3>Markdown formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">basics</a>, <a target="_new" href="http://michelf.com/projects/php-markdown/extra/">extended syntax</a>, <a target="_new" href="http://maruku.rubyforge.org/proposal.html">metadata</a>)</h3>
|
||||
<%= render(:file => "markdown_table") -%>
|
||||
<h3>Markdown formatting tips (<a target="_new" href="http://daringfireball.net/projects/markdown/syntax">basics</a>, <a target="_new" href="http://maruku.rubyforge.org/#extra">extended syntax</a>, <a target="_new" href="http://maruku.rubyforge.org/proposal.html">metadata</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>**your text**</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>`my code`</td><td class="arrow">→</td><td><code>my code</code></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td>1. Numbered list<br />1. Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>Definition list<br />: is useful</td><td class="arrow">→</td><td><dl style="display:inline"><dt>Definition list</dt><dd>is useful</dd></dl></td></tr>
|
||||
<tr><td>[link name](URL)</td><td class="arrow">→</td><td><a href="URL">link name</a></td></tr>
|
||||
<tr><td>![Alt text](URL)</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
<tr><td>## Header ##<br />### Subheader ###<br />#### Etc. ####</td><td class="arrow">→</td><td><b><span style="font-size:1.2em">Header</span><br /><span style="font-size:1.1em">Subheader</span><br /><span style="font-size:1em">Etc.</span></b></td></tr>
|
||||
<tr><td>***</td><td class="arrow">→</td><td>Horizontal ruler</td></tr>
|
||||
<tr><td><http://url><br /><email@add.com></td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
</table>
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>**your text**</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td>`my code`</td><td class="arrow">→</td><td><code>my code</code></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td>1. Numbered list<br />1. Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>Definition list<br />: is useful</td><td class="arrow">→</td><td><dl style="display:inline"><dt>Definition list</dt><dd>is useful</dd></dl></td></tr>
|
||||
<tr><td>[link name](URL)</td><td class="arrow">→</td><td><a href="URL">link name</a></td></tr>
|
||||
<tr><td>![Alt text](URL)</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
<tr><td>## Header ##<br />### Subheader ###<br />#### Subsubhead####<br/>##### Etc. #####</td><td class="arrow">→</td>
|
||||
<td><b><span style="font-size:1.73em">Header</span>
|
||||
<br/><span style="font-size:1.44em">Subheader</span><br/><span style="font-size:1.2em">Subsubhead</span>
|
||||
<br/><span style="font-size:1em">Etc.</span></b></td></tr>
|
||||
<tr><td>***</td><td class="arrow">→</td><td>Horizontal rule</td></tr>
|
||||
<tr><td>Some text[^fine] here.<br/>[^fine]: The fine print.</td><td class="arrow">→</td><td>A footnote</td></tr>
|
||||
<tr><td>*[LA]: Los Angeles<br/>*[ppm]: parts per million</td><td class="arrow">→</td><td>Abbreviations</td></tr>
|
||||
</table>
|
|
@ -9,8 +9,7 @@ end
|
|||
%>
|
||||
|
||||
<div class="navigation">
|
||||
<span class="skipNav"><a href='#navEnd'>Skip the Navigation Links</a> | </span>
|
||||
<%- if params['action'] != 'published' then -%>
|
||||
<% if params['action'] != 'published' then %>
|
||||
<%= list_item 'Home Page', {:action => 'show', :id => 'HomePage'}, 'Home, Sweet Home', 'H' %> |
|
||||
<%= list_item 'All Pages', {:action => 'list'}, 'Alphabetically sorted list of pages', 'A' %> |
|
||||
<%= list_item 'Recently Revised', {:action =>'recently_revised'}, 'Pages sorted by when they were last changed', 'U' %> |
|
||||
|
@ -23,9 +22,8 @@ end
|
|||
onfocus="this.value == 'Search' ? this.value = '' : true"
|
||||
onblur="this.value == '' ? this.value = 'Search' : true" /></fieldset>
|
||||
<% end %>
|
||||
<%- else -%>
|
||||
<%= list_item 'Home Page', {:action => 'published', :id => 'HomePage'}, 'Home, Sweet Home', 'H' %> |
|
||||
<% else %>
|
||||
<%= list_item 'Home Page', {:action => 'published', :id => 'HomePage'}, 'Home, Sweet Home', 'H' %> |
|
||||
<%= list_item 'Feeds', {:action => 'feeds'}, 'Subscribe to changes by Atom' %>
|
||||
<% end-%>
|
||||
<span id='navEnd'></span>
|
||||
<% end%>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<span id="svg_logo"><svg version="1.1" width="100%" height="100%" viewBox='0 -1 180 198' xmlns='http://www.w3.org/2000/svg'>
|
||||
<span id="svg_logo"><svg version="1.1" width="100%" height="100%" viewBox='0 0 180 197' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path id="svg_logo_path" fill="#<%= @web ? @web.color : "393" %>" stroke-width='0.5' stroke='#000' d='
|
||||
M170,60c4,11-1,20-12,25c-9,4-25,3-20,15c5,5,15,0,24,1c11,1,21,11,14,21c-10,15-35,6-48-1c-5-3-27-23-32-10c-1,13,15,10,22,16
|
||||
c11,4,24,14,34,20c12,10,7,25-9,23c-11-1-22-9-30-16c-5-5-13-18-21-9c-2,6,2,11,5,14c9,9,22,14,22,31c-2,8-12,8-18,4c-4-3-9-8-11-13
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
<h3>Textile formatting tips (<a href="http://redcloth.org/hobix.com/textile/quick.html" onclick="quickRedReference(); return false;">advanced</a>)</h3>
|
||||
<h3>Textile formatting tips (<a href="http://hobix.com/textile/quick.html" onclick="quickRedReference(); return false;">advanced</a>)</h3>
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tr><td>_your text_</td><td class="arrow">→</td><td><em>your text</em></td></tr>
|
||||
<tr><td>*your text*</td><td class="arrow">→</td><td><strong>your text</strong></td></tr>
|
||||
<tr><td style="padding-right:0.75em">%{color:red}hello%</td><td class="arrow">→</td><td><span style="color: red;">hello</span></td></tr>
|
||||
<tr><td>%{color:red}hello%</td><td class="arrow">→</td><td><span style="color: red;">hello</span></td></tr>
|
||||
<tr><td>* Bulleted list<br />* Second item</td><td class="arrow">→</td><td>• Bulleted list<br />• Second item</td></tr>
|
||||
<tr><td># Numbered list<br /># Second item</td><td class="arrow">→</td><td>1. Numbered list<br />2. Second item</td></tr>
|
||||
<tr><td>"linkname":URL</td><td class="arrow">→</td><td><a href="URL">linkname</a></td></tr>
|
||||
<tr><td>|a|table|row|<br />|b|table|row|</td><td class="arrow">→</td><td>Table</td></tr>
|
||||
<tr><td>http://url<br />email@address.com</td><td class="arrow">→</td><td>Auto-linked</td></tr>
|
||||
<tr><td>!imageURL!</td><td class="arrow">→</td><td>Image</td></tr>
|
||||
</table>
|
||||
|
||||
<script language="JavaScript">
|
||||
function quickRedReference() {
|
||||
window.open(
|
||||
"http://redcloth.org/hobix.com/textile/quick.html",
|
||||
"http://hobix.com/textile/quick.html",
|
||||
"redRef",
|
||||
"height=600,width=550,channelmode=0,dependent=0," +
|
||||
"directories=0,fullscreen=0,location=0,menubar=0," +
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<%- unless @page.linked_from.empty? -%>
|
||||
<span class="linked">
|
||||
| Linked from:
|
||||
<%= @page.linked_from.collect { |referring_page| link_to_existing_page referring_page }.join(", ").html_safe %>
|
||||
<%= @page.linked_from.collect { |referring_page| link_to_existing_page referring_page }.join(", ") %>
|
||||
</span>
|
||||
<%- end -%>
|
||||
|
||||
<%- unless @page.included_from.empty? -%>
|
||||
<span class="linked">
|
||||
| Included from:
|
||||
<%= @page.included_from.collect { |referring_page| link_to_existing_page referring_page }.join(", ").html_safe %>
|
||||
<%= @page.included_from.collect { |referring_page| link_to_existing_page referring_page }.join(", ") %>
|
||||
</span>
|
||||
<%- end -%>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<li>
|
||||
<%= link_to_page author.purify %>
|
||||
co- or authored:
|
||||
<%= raw @page_names_by_author[author].collect { |page_name| link_to_page(page_name) }.sort.join ', ' %>
|
||||
<%= @page_names_by_author[author].collect { |page_name| link_to_page(page_name) }.sort.join ', ' %>
|
||||
</li>
|
||||
<%- end -%>
|
||||
</ul>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<%-
|
||||
@title = "Editing #{@page.name.escapeHTML}".html_safe
|
||||
@title = "Editing #{@page.name.escapeHTML}"
|
||||
@content_width = 720
|
||||
@hide_navigation = true
|
||||
-%>
|
||||
|
||||
<div id="MarkupHelp">
|
||||
<%= render(:file => "#{@web.markup}_help") -%>
|
||||
<%= render(:file => 'wiki_words_help') unless @web.brackets_only? -%>
|
||||
<%= render(:file => "#{@web.markup}_help") %>
|
||||
<%= render(:file => 'wiki_words_help') %>
|
||||
</div>
|
||||
|
||||
<% form_tag({ :action => 'save', :web => @web.address, :id => @page.name },
|
||||
|
@ -14,7 +14,7 @@
|
|||
'accept-charset' => 'utf-8' }) do %>
|
||||
<div>
|
||||
<textarea name="content" id="content" rows="24" cols="60"><%= h(flash[:content] ||
|
||||
(params['content'] ? params['content'] : @page.content).purify) %></textarea>
|
||||
((params['content'] && params['content'].is_utf8?) ? params['content'] : @page.content).purify) %></textarea>
|
||||
<% if @page_name != 'HomePage' -%>
|
||||
<p>
|
||||
<%= check_box_tag :alter_title, value = "1", checked=false,
|
||||
|
@ -46,19 +46,18 @@ function toggleVisibility() {
|
|||
var span = document.getElementById('title_change');
|
||||
if (span.style.display =='inline') {
|
||||
span.style.display ='none';
|
||||
document.getElementById('new_name').value = "<%= escape_javascript(@page.name) %>";
|
||||
document.getElementById('new_name').value = "<%= @page.name %>";
|
||||
var content = document.getElementById('content').value
|
||||
document.getElementById('content').value = content.replace(/\[\[!redirects <%= Regexp.escape(@page.name).gsub('/', '\\/') %>\]\]\n/, '')
|
||||
document.getElementById('content').value = content.replace(/\[\[!redirects <%= Regexp.escape(@page.name) %>\]\]\n/, '')
|
||||
}
|
||||
else
|
||||
span.style.display ='inline'
|
||||
}
|
||||
|
||||
function addRedirect(){
|
||||
var e = document.getElementById('new_name').value;
|
||||
if ( e != "<%= escape_javascript(@page.name) %>" && e != '') {
|
||||
if (document.getElementById('new_name').value != "<%= @page.name %>" ) {
|
||||
var content = document.getElementById('content');
|
||||
content.value = '[[!redirects <%= escape_javascript(@page.name) %>]]\n' + content.value
|
||||
content.value = '[[!redirects <%= @page.name %>]]\n' + content.value
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,11 +68,5 @@ function cleanAuthorName() {
|
|||
}
|
||||
|
||||
document.forms["editForm"].elements["content"].focus();
|
||||
<%- if [:markdownMML, :markdownPNG, :markdown].include?(@web.markup) -%>
|
||||
setupSVGedit('<%= compute_public_path("editor/svg-editor.html", "svg-edit").split(/\?/)[0] %>');
|
||||
<%- unless @page.categories.include?('S5-slideshow') -%>
|
||||
addS5button('<%= @page.name.escapeHTML %>');
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
//--><!]]>
|
||||
</script>
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<a href="<%= url_for :web => @web.address, :action => 'files',
|
||||
:id => file.file_name %>"><%= file.file_name%></a> (<%= file.created_at.asctime %>) <span class="linked"><%= "Linked to by: " unless
|
||||
@web.pages_that_link_to_file(file.file_name).empty? -%>
|
||||
<%= @web.pages_that_link_to_file(file.file_name).collect { |referring_page| link_to_page(referring_page) }.join(", ").html_safe %></span>
|
||||
<%= @web.pages_that_link_to_file(file.file_name).collect { |referring_page| link_to_page(referring_page) }.join(", ") %></span>
|
||||
</li>
|
||||
<%- end -%>
|
||||
</ul>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<%- @title = @page.plain_name + " (history)".html_safe -%>
|
||||
<%- @title = @page.plain_name + " (history)" -%>
|
||||
<%- @show_footer = true -%>
|
||||
|
||||
<%- @revisions_by_day.keys.sort.reverse.each do |day| -%>
|
||||
|
@ -7,7 +7,7 @@
|
|||
<%- for rev in @revisions_by_day[day] -%>
|
||||
<li>
|
||||
<%= link_to_revision(rev.page, @revision_numbers[rev.id],
|
||||
text= (rev.page.rev_ids.size == @revision_numbers[rev.id] ?
|
||||
text= (rev.page.revisions.length == @revision_numbers[rev.id] ?
|
||||
"Current" :
|
||||
"Revision #{@revision_numbers[rev.id]}" )
|
||||
) %>
|
||||
|
|
|
@ -6,34 +6,10 @@
|
|||
<%- unless @pages_that_are_orphaned.empty? && @page_names_that_are_wanted.empty? -%>
|
||||
<h2>
|
||||
All Pages
|
||||
<br/><span class="pageType">All pages in <%= raw @set_name %> listed alphabetically</span>
|
||||
<br/><span class="pageType">All pages in <%= @set_name %> listed alphabetically</span>
|
||||
</h2>
|
||||
<%- end -%>
|
||||
|
||||
<% if [:markdownMML, :markdownPNG, :markdown].include?(@web.markup) -%>
|
||||
<% form_tag({ :controller => 'wiki', :action => 'tex_list', :web => @web.address },
|
||||
{'method' => 'post', 'accept-charset' => 'utf-8' }) do
|
||||
%>
|
||||
<div>
|
||||
<% if params['category'] -%>
|
||||
<input type="hidden" name="category" value="<%= params['category'] %>"/>
|
||||
<%- end -%>
|
||||
<ul id="sortable_pages">
|
||||
<% @pages_in_category.each do |page| %>
|
||||
<% content_tag_for :li, page do %>
|
||||
|
||||
<input type="checkbox" name="<%= page.name %>" value="tex"/>
|
||||
<%= link_to_existing_page page, truncate(page.plain_name, :length => 35) %>
|
||||
<% end %>
|
||||
|
||||
<% end %>
|
||||
</ul>
|
||||
<%= sortable_element('sortable_pages', {:onUpdate => 'function(){}'}) %>
|
||||
<label for="commit"> Export selected pages (drag to re-order them) to a LaTeX file.</label>
|
||||
<%= submit_tag("Export") %>
|
||||
</div>
|
||||
<%- end -%>
|
||||
<%- else -%>
|
||||
<ul>
|
||||
<%- @pages_in_category.each do |page| -%>
|
||||
<li>
|
||||
|
@ -41,7 +17,6 @@
|
|||
</li>
|
||||
<%- end -%>
|
||||
</ul>
|
||||
<%- end -%>
|
||||
|
||||
<%- if @web.count_pages? -%>
|
||||
<% total_chars = @pages_in_category.characters %>
|
||||
|
@ -55,7 +30,7 @@
|
|||
Wanted Pages
|
||||
<br/>
|
||||
<span class="pageType">
|
||||
Nonexistent pages that other pages in <%= raw @set_name %> reference
|
||||
Nonexistent pages that other pages in <%= @set_name %> reference
|
||||
</span>
|
||||
</h2>
|
||||
|
||||
|
@ -66,7 +41,7 @@
|
|||
wanted by
|
||||
<%= @web.select.pages_that_reference(wanted_page_name).collect { |referring_page|
|
||||
link_to_existing_page referring_page
|
||||
}.join(", ").html_safe
|
||||
}.join(", ")
|
||||
%>
|
||||
</li>
|
||||
<%- end -%>
|
||||
|
@ -76,7 +51,7 @@
|
|||
<%- unless @pages_that_are_orphaned.empty? -%>
|
||||
<h2>
|
||||
Orphaned Pages
|
||||
<br/><span class="pageType">Pages in <%= raw @set_name %> that no other page reference</span>
|
||||
<br/><span class="pageType">Pages in <%= @set_name %> that no other page reference</span>
|
||||
</h2>
|
||||
|
||||
<ul style="margin-bottom: 35px">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<%- @title = "#{@page.plain_name} is locked".html_safe -%>
|
||||
<%- @title = "#{@page.plain_name} is locked" -%>
|
||||
|
||||
<p>
|
||||
<%= link_to_page(h(@page.locked_by.purify)) %>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<%-
|
||||
@title = "Creating #{WikiWords.separate(@page_name).escapeHTML}".html_safe
|
||||
@title = "Creating #{CGI.escapeHTML(WikiWords.separate(@page_name))}"
|
||||
@content_width = 720
|
||||
@hide_navigation = true
|
||||
-%>
|
||||
|
||||
<div id="MarkupHelp">
|
||||
<%= render(:file => "#{@web.markup}_help") -%>
|
||||
<%= render(:file => 'wiki_words_help') unless @web.brackets_only? -%>
|
||||
<%= render(:file => "#{@web.markup}_help") %>
|
||||
<%= render(:file => 'wiki_words_help') %>
|
||||
</div>
|
||||
|
||||
<div id="editForm">
|
||||
|
@ -14,7 +14,7 @@
|
|||
{ 'id' => 'editForm', 'method' => 'post', 'onsubmit' => 'cleanAuthorName();', 'accept-charset' => 'utf-8' }) do %>
|
||||
|
||||
<textarea name="content" id="content" rows="24" cols="60"><%= h(flash[:content] ||
|
||||
params['content'] ? params['content'].purify : '' ) %></textarea>
|
||||
( (params['content'] && params['content'].is_utf8?) ? params['content'] : '').purify ) %></textarea>
|
||||
<div id="editFormButtons">
|
||||
<input type="submit" value="Submit" accesskey="s"/> as
|
||||
<%= text_field_tag :author, @author,
|
||||
|
@ -31,8 +31,4 @@ function cleanAuthorName() {
|
|||
}
|
||||
}
|
||||
document.forms["editForm"].elements["content"].focus();
|
||||
<%- if [:markdownMML, :markdownPNG, :markdown].include?(@web.markup) -%>
|
||||
setupSVGedit('<%= compute_public_path("editor/svg-editor.html", "svg-edit").split(/\?/)[0] %>');
|
||||
addS5button('<%= @page_name.escapeHTML %>');
|
||||
<%- end -%>
|
||||
</script>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<%-
|
||||
@title = @page.plain_name
|
||||
@title += ' (changes)' if @show_diff
|
||||
@title += ' (changes)' if @show_diff
|
||||
@show_footer = true
|
||||
-%>
|
||||
|
||||
<div id="revision">
|
||||
<%- if @show_diff -%>
|
||||
<p class="show_diff">
|
||||
Showing changes from revision #<%= @page.rev_ids.size - 1 %> to #<%= @page.rev_ids.size %>:
|
||||
Showing changes from revision #<%= @page.revisions.size - 1 %> to #<%= @page.revisions.size %>:
|
||||
<ins class="diffins">Added</ins> | <del class="diffdel">Removed</del> | <del class="diffmod">Chan</del><ins class="diffmod">ged</ins>
|
||||
</p>
|
||||
<%= @renderer.display_diff %>
|
||||
|
@ -18,7 +18,7 @@
|
|||
|
||||
<div class="byline">
|
||||
<%= @page.revisions? ? "Revised" : "Created" %> on <%= format_date(@page.revised_at) %>
|
||||
by <%= author_link(@page) %>
|
||||
by <%= author_link(@page).purify %>
|
||||
<%= "(#{@page.author.ip})" if @page.author.respond_to?(:ip) %>
|
||||
<% if @web.count_pages? %>
|
||||
<% total_chars = @page.content.length %>
|
||||
|
@ -28,7 +28,7 @@
|
|||
|
||||
<div class="navigation navfoot">
|
||||
|
||||
<%= raw navigation_menu_for_page.join(' | ') %>
|
||||
<%= navigation_menu_for_page.join(' | ') %>
|
||||
|
||||
<span class="views">
|
||||
| Views:
|
||||
|
@ -45,9 +45,6 @@
|
|||
{:id => 'view_S5'} %>
|
||||
<%- end -%>
|
||||
<%- end -%>
|
||||
|
|
||||
<%= link_to 'Source', {:web => @web.address, :action => 'source', :id => @page.name},
|
||||
{:id => 'view_source', :rel => 'nofollow' } %>
|
||||
</span>
|
||||
|
||||
<%= render :partial => 'inbound_links' %>
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
@title = @page.plain_name
|
||||
@hide_navigation = true
|
||||
@style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
|
||||
@inline_style = true
|
||||
-%>
|
||||
|
||||
<%= @renderer.display_content %>
|
||||
<%= @renderer.display_content_for_export %>
|
||||
|
||||
<div class="byline">
|
||||
<%= @page.revisions? ? "Revised" : "Created" %> on <%= format_date(@page.revised_at) %>
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
<%- for page in @pages_by_day[day] -%>
|
||||
<li>
|
||||
<%= link_to_existing_page page %>
|
||||
<%- if page.rev_ids.size > 1 %>
|
||||
<%- if page.revisions.length > 1 %>
|
||||
<span class="views">
|
||||
( <%= link_to_revision(page, page.rev_ids.size, text='diff',
|
||||
( <%= link_to_revision(page, page.revisions.length, text='diff',
|
||||
mode='diff') %> | <%= link_to_history(page, text='history') %> )
|
||||
</span>
|
||||
<%- end -%>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<%-
|
||||
@title = "#{@page.plain_name} (Rev ##{@revision_number}#{@show_diff ? ', changes' : ''})".html_safe
|
||||
@title = "#{@page.plain_name} (Rev ##{@revision_number}#{@show_diff ? ', changes' : ''})"
|
||||
-%>
|
||||
|
||||
|
||||
|
@ -21,11 +21,6 @@
|
|||
</div>
|
||||
|
||||
<div class="navigation navfoot">
|
||||
<%= raw navigation_menu_for_revision.join(' | ') %>
|
||||
<span class="views">
|
||||
| View:
|
||||
<%= link_to 'Source', {:web => @web.address, :action => 'source', :id => @page.name, :rev => @revision_number},
|
||||
{:id => 'view_source', :rel => 'nofollow' } %>
|
||||
</span>
|
||||
<%= navigation_menu_for_revision.join(' | ') %>
|
||||
<%= render :partial => 'inbound_links' %>
|
||||
</div>
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
<%-
|
||||
@title = "Rollback to #{@page.plain_name} Rev ##{@revision_number}".html_safe
|
||||
@title = "Rollback to #{@page.plain_name} Rev ##{@revision_number}"
|
||||
@content_width = 720
|
||||
@hide_navigation = true
|
||||
-%>
|
||||
|
||||
<%= "<p style='color:red'>Please correct the error that caused this error in rendering:<br/><small>#{params["msg"]}</small></p>" if params["msg"] %>
|
||||
|
||||
<div id="MarkupHelp">
|
||||
<%= render(:file => "#{@web.markup}_help") -%>
|
||||
<%= render(:file => 'wiki_words_help') unless @web.brackets_only? -%>
|
||||
<%= render(:file => "#{@web.markup}_help") %>
|
||||
<%= render(:file => 'wiki_words_help') %>
|
||||
</div>
|
||||
|
||||
<% form_tag({:web => @web.address, :action => 'save', :id => @page.name},
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg-flat.dtd" >
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<head>
|
||||
|
@ -12,33 +13,15 @@
|
|||
<meta name="defaultView" content="slideshow" />
|
||||
<meta name="controlVis" content="hidden" />
|
||||
<!-- style sheet links -->
|
||||
<%= stylesheet_link_tag "/stylesheets/syntax.css", :media => 'all', :id => 'syntaxStyle' %>
|
||||
<%= stylesheet_link_tag "/s5/ui/core/outline.css", :media => 'screen', :id => 'outlineStyle' %>
|
||||
<%= stylesheet_link_tag "/s5/ui/core/print.css", :media => 'print', :id => 'slidePrint' %>
|
||||
<%= stylesheet_link_tag "/s5/ui/core/opera.css", :media => 'projection', :id => 'operaFix' %>
|
||||
<%= stylesheet_link_tag "/s5/ui/core/math.css", :media => 'all', :id => 'mathStyle' %>
|
||||
<%= stylesheet_link_tag "/s5/themes/#{@s5_theme}/slides.css", :media => 'projection', :id => 'slideProj' %>
|
||||
<link rel="stylesheet" href="/s5/themes/<%=@s5_theme%>/slides.css" type="text/css" media="projection" id="slideProj" />
|
||||
<link rel="stylesheet" href="/s5/ui/core/outline.css" type="text/css" media="screen" id="outlineStyle" />
|
||||
<link rel="stylesheet" href="/s5/ui/core/print.css" type="text/css" media="print" id="slidePrint" />
|
||||
<link rel="stylesheet" href="/s5/ui/core/opera.css" type="text/css" media="projection" id="operaFix" />
|
||||
<link rel="stylesheet" href="/s5/ui/core/math.css" type="text/css" media="all" id="mathStyle" />
|
||||
|
||||
<!-- S5 JS -->
|
||||
<%= javascript_include_tag 'prototype' %>
|
||||
<%- if @web.markup == :markdownMML -%>
|
||||
<script type="text/x-mathjax-config">
|
||||
MathJax.Hub.Config({
|
||||
MathML: { useMathMLspacing: true },
|
||||
"HTML-CSS": { scale: 90 }
|
||||
});
|
||||
if (window._onload_fired_) MathJax.Hub.Startup.onload();
|
||||
</script>
|
||||
<script type="text/javascript">
|
||||
if (!(Prototype.Browser.Gecko || navigator.userAgent.match(/MathPlayer/))) {
|
||||
var s = document.createElement('script');
|
||||
s.src = "<%= compute_public_path('MathJax.js', 'MathJax').split('?')[0] %>?config=MML_HTMLorMML";
|
||||
document.querySelector('head').appendChild(s);
|
||||
window.addEventListener("load", function(){window._onload_fired_ = true} , false);
|
||||
};
|
||||
</script>
|
||||
<%- end -%>
|
||||
<%= javascript_include_tag "/s5/ui/core/slides.js" %>
|
||||
<script src="/s5/ui/core/slides.js" type="text/javascript"></script>
|
||||
<script src="/javascripts/prototype.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<%- @title = "Search results for \"#{h @query}\"".html_safe -%>
|
||||
<%- @title = "Search results for \"#{h params["query"]}\"" -%>
|
||||
|
||||
<%- unless @title_results.empty? -%>
|
||||
<h2><%= @title_results.length %> page(s) containing search string in the page name:</h2>
|
||||
|
@ -24,7 +24,7 @@
|
|||
<%- end -%>
|
||||
|
||||
<%- if (@results + @title_results).empty? -%>
|
||||
<h2>No pages contain "<%= h(@query).html_safe %>" </h2>
|
||||
<h2>No pages contain "<%= h params["query"] %>" </h2>
|
||||
<p>
|
||||
Perhaps you should try expanding your query. Remember that Instiki searches for entire
|
||||
phrases, so if you search for "all that jazz" it will not match pages that contain these
|
||||
|
@ -35,7 +35,4 @@
|
|||
expression. That's actually what Instiki uses, so go right ahead and flex your
|
||||
"[a-z]*Leet?RegExpSkill(s|z)"
|
||||
</p>
|
||||
<p>
|
||||
<b>Create a new page, named:</b> "<span class='newWikiWord'><%= link_to h(@query).html_safe, :web => @web.address, :action => 'new', :id => @query %></span>"
|
||||
</p>
|
||||
<%- end -%>
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN" "http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg-flat.dtd" >
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>
|
||||
<%- if @page and (@page.name == 'HomePage') -%>
|
||||
<%= h(@web.name) %>
|
||||
<%- else @web -%>
|
||||
<%= @page.plain_name %> in <%= h @web.name %>
|
||||
<%- end -%>
|
||||
</title>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<meta name="robots" content="<%= @robots_metatag_value %>" />
|
||||
|
||||
<style type="text/css">
|
||||
h1#pageName, div.info, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, [actiontype="toggle"]:hover, #TextileHelp h3 {
|
||||
color: #<%= @web ? @web.color : "393" %>;
|
||||
}
|
||||
<%= Rails.root.join('public', 'stylesheets', 'instiki.css').read if @inline_style %>
|
||||
</style>
|
||||
<%= stylesheet_link_tag 'instiki', :media => 'all' unless @inline_style %>
|
||||
<%= "<style type='text/css'>#{@style_additions}</style>".html_safe if @style_additions %>
|
||||
<style type="text/css"><!--/*--><![CDATA[/*><!--*/
|
||||
<%= @web ? @web.additional_style : '' %>
|
||||
/*]]>*/--></style>
|
||||
<%= javascript_include_tag :defaults %>
|
||||
<script type="text/javascript">
|
||||
<!--//--><![CDATA[//><!--
|
||||
function updateSize(elt, w, h) {
|
||||
// adjust to the size of the user's browser area.
|
||||
// w and h are the original, unadjusted, width and height per row/column
|
||||
var parentheight = document.viewport.getHeight();
|
||||
var parentwidth = $('Container').getWidth();
|
||||
elt.writeAttribute({'cols': Math.floor(parentwidth/w) - 1,
|
||||
'rows': Math.floor(parentheight/h) - 2 });
|
||||
elt.setStyle({Width: parentwidth, Height: parentheight});
|
||||
}
|
||||
|
||||
function resizeableTextarea() {
|
||||
//make the textarea resize to fit available space
|
||||
$$('textarea#content').each( function(textarea) {
|
||||
var w = textarea.getWidth()/textarea.getAttribute('cols');
|
||||
var h = textarea.getStyle('lineHeight').replace(/(\d*)px/, "$1");
|
||||
Event.observe(window, 'resize', function(){ updateSize(textarea, w, h) });
|
||||
updateSize(textarea, w, h);
|
||||
Form.Element.focus(textarea);
|
||||
});
|
||||
}
|
||||
|
||||
window.onload = function (){
|
||||
resizeableTextarea();
|
||||
}
|
||||
//--><!]]>
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div id="Container">
|
||||
<textarea id='content' readonly=' readonly' rows='24' cols='60' ><%= (@revision ? @revision.content : @page.content).purify %></textarea>
|
||||
</div> <!-- Container -->
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -1,3 +1,239 @@
|
|||
\documentclass[12pt,titlepage]{article}
|
||||
|
||||
\usepackage{amsmath}
|
||||
\usepackage{amsfonts}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{amsthm}
|
||||
<%- if @tex_content =~ /\\mathbb\{[^}]*[a-z0-9]+[^}]*\}/ -%>
|
||||
\usepackage{mathbbol}
|
||||
<% end -%>
|
||||
\usepackage{graphicx}
|
||||
\usepackage{color}
|
||||
\usepackage{ucs}
|
||||
\usepackage[utf8x]{inputenc}
|
||||
\usepackage{hyperref}
|
||||
|
||||
%----Macros----------
|
||||
%
|
||||
% Unresolved issues:
|
||||
%
|
||||
% \righttoleftarrow
|
||||
% \lefttorightarrow
|
||||
%
|
||||
% \color{} with HTML colorspec
|
||||
% \bgcolor
|
||||
% \array
|
||||
|
||||
% Of the standard HTML named colors, white, black, red, green, blue and yellow
|
||||
% are predefined in the color package. Here are the rest.
|
||||
\definecolor{aqua}{rgb}{0, 1.0, 1.0}
|
||||
\definecolor{fuschia}{rgb}{1.0, 0, 1.0}
|
||||
\definecolor{gray}{rgb}{0.502, 0.502, 0.502}
|
||||
\definecolor{lime}{rgb}{0, 1.0, 0}
|
||||
\definecolor{maroon}{rgb}{0.502, 0, 0}
|
||||
\definecolor{navy}{rgb}{0, 0, 0.502}
|
||||
\definecolor{olive}{rgb}{0.502, 0.502, 0}
|
||||
\definecolor{purple}{rgb}{0.502, 0, 0.502}
|
||||
\definecolor{silver}{rgb}{0.753, 0.753, 0.753}
|
||||
\definecolor{teal}{rgb}{0, 0.502, 0.502}
|
||||
|
||||
% Because of conflicts, \space and \mathop are converted to
|
||||
% \itexspace and \operatorname during preprocessing.
|
||||
|
||||
% itex: \space{ht}{dp}{wd}
|
||||
%
|
||||
% Height and baseline depth measurements are in units of tenths of an ex while
|
||||
% the width is measured in tenths of an em.
|
||||
\makeatletter
|
||||
\newdimen\itex@wd%
|
||||
\newdimen\itex@dp%
|
||||
\newdimen\itex@thd%
|
||||
\def\itexspace#1#2#3{\itex@wd=#3em%
|
||||
\itex@wd=0.1\itex@wd%
|
||||
\itex@dp=#2ex%
|
||||
\itex@dp=0.1\itex@dp%
|
||||
\itex@thd=#1ex%
|
||||
\itex@thd=0.1\itex@thd%
|
||||
\advance\itex@thd\the\itex@dp%
|
||||
\makebox[\the\itex@wd]{\rule[-\the\itex@dp]{0cm}{\the\itex@thd}}}
|
||||
\makeatother
|
||||
|
||||
% \tensor and \multiscript
|
||||
\makeatletter
|
||||
\newif\if@sup
|
||||
\newtoks\@sups
|
||||
\def\append@sup#1{\edef\act{\noexpand\@sups={\the\@sups #1}}\act}%
|
||||
\def\reset@sup{\@supfalse\@sups={}}%
|
||||
\def\mk@scripts#1#2{\if #2/ \if@sup ^{\the\@sups}\fi \else%
|
||||
\ifx #1_ \if@sup ^{\the\@sups}\reset@sup \fi {}_{#2}%
|
||||
\else \append@sup#2 \@suptrue \fi%
|
||||
\expandafter\mk@scripts\fi}
|
||||
\def\tensor#1#2{\reset@sup#1\mk@scripts#2_/}
|
||||
\def\multiscripts#1#2#3{\reset@sup{}\mk@scripts#1_/#2%
|
||||
\reset@sup\mk@scripts#3_/}
|
||||
\makeatother
|
||||
|
||||
% \slash
|
||||
\makeatletter
|
||||
\newbox\slashbox \setbox\slashbox=\hbox{$/$}
|
||||
\def\itex@pslash#1{\setbox\@tempboxa=\hbox{$#1$}
|
||||
\@tempdima=0.5\wd\slashbox \advance\@tempdima 0.5\wd\@tempboxa
|
||||
\copy\slashbox \kern-\@tempdima \box\@tempboxa}
|
||||
\def\slash{\protect\itex@pslash}
|
||||
\makeatother
|
||||
|
||||
% Renames \sqrt as \oldsqrt and redefine root to result in \sqrt[#1]{#2}
|
||||
\let\oldroot\root
|
||||
\def\root#1#2{\oldroot #1 \of{#2}}
|
||||
|
||||
% Manually declare the txfonts symbolsC font
|
||||
\DeclareSymbolFont{symbolsC}{U}{txsyc}{m}{n}
|
||||
\SetSymbolFont{symbolsC}{bold}{U}{txsyc}{bx}{n}
|
||||
\DeclareFontSubstitution{U}{txsyc}{m}{n}
|
||||
|
||||
% Manually declare the stmaryrd font
|
||||
\DeclareSymbolFont{stmry}{U}{stmry}{m}{n}
|
||||
\SetSymbolFont{stmry}{bold}{U}{stmry}{b}{n}
|
||||
|
||||
% Declare specific arrows from txfonts without loading the full package
|
||||
\makeatletter
|
||||
\def\re@DeclareMathSymbol#1#2#3#4{%
|
||||
\let#1=\undefined
|
||||
\DeclareMathSymbol{#1}{#2}{#3}{#4}}
|
||||
\re@DeclareMathSymbol{\neArrow}{\mathrel}{symbolsC}{116}
|
||||
\re@DeclareMathSymbol{\neArr}{\mathrel}{symbolsC}{116}
|
||||
\re@DeclareMathSymbol{\seArrow}{\mathrel}{symbolsC}{117}
|
||||
\re@DeclareMathSymbol{\seArr}{\mathrel}{symbolsC}{117}
|
||||
\re@DeclareMathSymbol{\nwArrow}{\mathrel}{symbolsC}{118}
|
||||
\re@DeclareMathSymbol{\nwArr}{\mathrel}{symbolsC}{118}
|
||||
\re@DeclareMathSymbol{\swArrow}{\mathrel}{symbolsC}{119}
|
||||
\re@DeclareMathSymbol{\swArr}{\mathrel}{symbolsC}{119}
|
||||
\re@DeclareMathSymbol{\nequiv}{\mathrel}{symbolsC}{46}
|
||||
\re@DeclareMathSymbol{\Perp}{\mathrel}{symbolsC}{121}
|
||||
\re@DeclareMathSymbol{\Vbar}{\mathrel}{symbolsC}{121}
|
||||
\re@DeclareMathSymbol{\sslash}{\mathrel}{stmry}{12}
|
||||
\re@DeclareMathSymbol{\invamp}{\mathrel}{symbolsC}{77}
|
||||
\re@DeclareMathSymbol{\parr}{\mathrel}{symbolsC}{77}
|
||||
\makeatother
|
||||
|
||||
% Widecheck
|
||||
\makeatletter
|
||||
\DeclareRobustCommand\widecheck[1]{{\mathpalette\@widecheck{#1}}}
|
||||
\def\@widecheck#1#2{%
|
||||
\setbox\z@\hbox{\m@th$#1#2$}%
|
||||
\setbox\tw@\hbox{\m@th$#1%
|
||||
\widehat{%
|
||||
\vrule\@width\z@\@height\ht\z@
|
||||
\vrule\@height\z@\@width\wd\z@}$}%
|
||||
\dp\tw@-\ht\z@
|
||||
\@tempdima\ht\z@ \advance\@tempdima2\ht\tw@ \divide\@tempdima\thr@@
|
||||
\setbox\tw@\hbox{%
|
||||
\raise\@tempdima\hbox{\scalebox{1}[-1]{\lower\@tempdima\box
|
||||
\tw@}}}%
|
||||
{\ooalign{\box\tw@ \cr \box\z@}}}
|
||||
\makeatother
|
||||
|
||||
% udots (taken from yhmath)
|
||||
\makeatletter
|
||||
\def\udots{\mathinner{\mkern2mu\raise\p@\hbox{.}
|
||||
\mkern2mu\raise4\p@\hbox{.}\mkern1mu
|
||||
\raise7\p@\vbox{\kern7\p@\hbox{.}}\mkern1mu}}
|
||||
\makeatother
|
||||
|
||||
%% Renaming existing commands
|
||||
\newcommand{\underoverset}[3]{\underset{#1}{\overset{#2}{#3}}}
|
||||
\newcommand{\widevec}{\overrightarrow}
|
||||
\newcommand{\darr}{\downarrow}
|
||||
\newcommand{\nearr}{\nearrow}
|
||||
\newcommand{\nwarr}{\nwarrow}
|
||||
\newcommand{\searr}{\searrow}
|
||||
\newcommand{\swarr}{\swarrow}
|
||||
\newcommand{\curvearrowbotright}{\curvearrowright}
|
||||
\newcommand{\uparr}{\uparrow}
|
||||
\newcommand{\downuparrow}{\updownarrow}
|
||||
\newcommand{\duparr}{\updownarrow}
|
||||
\newcommand{\updarr}{\updownarrow}
|
||||
\newcommand{\gt}{>}
|
||||
\newcommand{\lt}{<}
|
||||
\newcommand{\map}{\mapsto}
|
||||
\newcommand{\embedsin}{\hookrightarrow}
|
||||
\newcommand{\Alpha}{A}
|
||||
\newcommand{\Beta}{B}
|
||||
\newcommand{\Zeta}{Z}
|
||||
\newcommand{\Eta}{H}
|
||||
\newcommand{\Iota}{I}
|
||||
\newcommand{\Kappa}{K}
|
||||
\newcommand{\Mu}{M}
|
||||
\newcommand{\Nu}{N}
|
||||
\newcommand{\Rho}{P}
|
||||
\newcommand{\Tau}{T}
|
||||
\newcommand{\Upsi}{\Upsilon}
|
||||
\newcommand{\omicron}{o}
|
||||
\newcommand{\lang}{\langle}
|
||||
\newcommand{\rang}{\rangle}
|
||||
\newcommand{\Union}{\bigcup}
|
||||
\newcommand{\Intersection}{\bigcap}
|
||||
\newcommand{\Oplus}{\bigoplus}
|
||||
\newcommand{\Otimes}{\bigotimes}
|
||||
\newcommand{\Wedge}{\bigwedge}
|
||||
\newcommand{\Vee}{\bigvee}
|
||||
\newcommand{\coproduct}{\coprod}
|
||||
\newcommand{\product}{\prod}
|
||||
\newcommand{\closure}{\overline}
|
||||
\newcommand{\integral}{\int}
|
||||
\newcommand{\doubleintegral}{\iint}
|
||||
\newcommand{\tripleintegral}{\iiint}
|
||||
\newcommand{\quadrupleintegral}{\iiiint}
|
||||
\newcommand{\conint}{\oint}
|
||||
\newcommand{\contourintegral}{\oint}
|
||||
\newcommand{\infinity}{\infty}
|
||||
\renewcommand{\empty}{\emptyset}
|
||||
\newcommand{\bottom}{\bot}
|
||||
\newcommand{\minusb}{\boxminus}
|
||||
\newcommand{\plusb}{\boxplus}
|
||||
\newcommand{\timesb}{\boxtimes}
|
||||
\newcommand{\intersection}{\cap}
|
||||
\newcommand{\union}{\cup}
|
||||
\newcommand{\Del}{\nabla}
|
||||
\newcommand{\odash}{\circleddash}
|
||||
\newcommand{\negspace}{\!}
|
||||
\newcommand{\widebar}{\overline}
|
||||
\newcommand{\textsize}{\normalsize}
|
||||
\renewcommand{\scriptsize}{\scriptstyle}
|
||||
\newcommand{\scriptscriptsize}{\scriptscriptstyle}
|
||||
\newcommand{\mathfr}{\mathfrak}
|
||||
\newcommand{\statusline}[2]{#2}
|
||||
\newcommand{\toggle}[2]{#1}
|
||||
|
||||
% Theorem Environments
|
||||
\theoremstyle{plain}
|
||||
\newtheorem{theorem}{Theorem}
|
||||
\newtheorem{lemma}{Lemma}
|
||||
\newtheorem{prop}{Proposition}
|
||||
\newtheorem{cor}{Corollary}
|
||||
\newtheorem*{utheorem}{Theorem}
|
||||
\newtheorem*{ulemma}{Lemma}
|
||||
\newtheorem*{uprop}{Proposition}
|
||||
\newtheorem*{ucor}{Corollary}
|
||||
\theoremstyle{definition}
|
||||
\newtheorem{defn}{Definition}
|
||||
\newtheorem{example}{Example}
|
||||
\newtheorem*{udefn}{Definition}
|
||||
\newtheorem*{uexample}{Example}
|
||||
\theoremstyle{remark}
|
||||
\newtheorem{remark}{Remark}
|
||||
\newtheorem{note}{Note}
|
||||
\newtheorem*{uremark}{Remark}
|
||||
\newtheorem*{unote}{Note}
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\begin{document}
|
||||
|
||||
%-------------------------------------------------------------------
|
||||
|
||||
\section*{<%= @page.name %>}
|
||||
|
||||
<%= @tex_content.html_safe %>
|
||||
<%= @tex_content %>
|
||||
|
||||
\end{document}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
<%= @tex_content.html_safe %>
|
|
@ -15,11 +15,11 @@
|
|||
<%- end -%>
|
||||
|
||||
<div class="byline" style="margin-bottom: 0px">
|
||||
<%= web.pages.size %> page<% if web.pages.size != 1 %>s<% end %> by <%= web.authors.size %> author<% if web.authors.size != 1 %>s<% end %>
|
||||
<%= web.pages.length %> page<% if web.pages.length != 1 %>s<% end %> by <%= web.authors.length %> author<% if web.authors.length != 1 %>s<% end %>
|
||||
- Last Update: <%= web.last_page.nil? ? format_date(web.created_at) : format_date(web.last_page.revised_at) %><br/>
|
||||
<%- if ! web.last_page.nil? -%>
|
||||
Last Document: <%= link_to_page(web.last_page.name,web) %>
|
||||
<%= web.last_page.revisions? ? "Revised" : "Created" %> by <%= author_link(web.last_page) %> (<%= web.last_page.current_revision.ip %>)
|
||||
<%= web.last_page.revisions? ? "Revised" : "Created" %> by <%= author_link(web.last_page).purify %> (<%= web.last_page.current_revision.ip %>)
|
||||
<%- end -%>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,262 +1,207 @@
|
|||
# == Introduction
|
||||
#
|
||||
# This module provides sanitization of XHTML+MathML+SVG
|
||||
# and of inline style attributes. Its genesis is {described here}[http://golem.ph.utexas.edu/~distler/blog/archives/001181.html].
|
||||
#
|
||||
# Uses the {HTML5lib parser}[http://code.google.com/p/html5lib/], so that the parsing behaviour should
|
||||
# resemble that of browsers.
|
||||
#
|
||||
# sanitize_xhtml() is a case-sensitive sanitizer, suitable for XHTML
|
||||
# sanitize_html() is a case-insensitive sanitizer suitable for HTML
|
||||
# sanitize_rexml() sanitizes a REXML tree, returning a string
|
||||
# safe_sanitize_xhtml() makes extra-sure that the result is well-formed XHTML
|
||||
# by running the output of sanitize_xhtml() through REXML
|
||||
#
|
||||
# == Files
|
||||
#
|
||||
# {sanitize.rb}[http://golem.ph.utexas.edu/~distler/code/instiki/svn/lib/sanitize.rb],
|
||||
# {HTML5lib}[http://golem.ph.utexas.edu/~distler/code/instiki/svn/vendor/plugins/HTML5lib/]
|
||||
#
|
||||
# == Author
|
||||
#
|
||||
# {Jacques Distler}[http://golem.ph.utexas.edu/~distler/]
|
||||
#
|
||||
# == License
|
||||
#
|
||||
# Ruby License
|
||||
|
||||
module Sanitize
|
||||
|
||||
require 'html5/html5parser'
|
||||
require 'html5/liberalxmlparser'
|
||||
require 'html5/treewalkers'
|
||||
require 'html5/treebuilders'
|
||||
require 'html5/serializer'
|
||||
require 'html5/sanitizer'
|
||||
require 'stringsupport.rb'
|
||||
# This module provides sanitization of XHTML+MathML+SVG
|
||||
# and of inline style attributes.
|
||||
#
|
||||
# Based heavily on Sam Ruby's code in the Universal FeedParser.
|
||||
|
||||
include HTML5
|
||||
require 'html/tokenizer'
|
||||
require 'node'
|
||||
|
||||
# Sanitize a string, parsed using XHTML parsing rules.
|
||||
#
|
||||
# :call-seq:
|
||||
# sanitize_xhtml(string) -> string
|
||||
# sanitize_xhtml(string, {:encoding => 'iso-8859-1', :to_tree => true}) -> REXML::Document
|
||||
#
|
||||
# Unless otherwise specified, the string is assumed to be utf-8 encoded.
|
||||
# By default, the output is a string. But, optionally, you can return a REXML tree.
|
||||
#
|
||||
# The string returned is utf-8 encoded. If you want, you can use iconv to convert it to some other encoding.
|
||||
# (REXML trees are always utf-8 encoded.)
|
||||
def sanitize_xhtml(html, options = {})
|
||||
@encoding = 'utf-8'
|
||||
@treebuilder = TreeBuilders::REXML::TreeBuilder
|
||||
@to_tree = false
|
||||
options.each do |name, value|
|
||||
next unless %w(encoding treebuilder to_tree).include? name.to_s
|
||||
if name.to_s == 'treebuilder'
|
||||
@treebuilder = HTML5lib::TreeBuilders.get_tree_builder(value)
|
||||
else
|
||||
instance_variable_set("@#{name}", value)
|
||||
end
|
||||
end
|
||||
if @encoding == 'utf-8'
|
||||
parsed = XHTMLParser.parse_fragment(html.to_utf8, {:tokenizer => HTMLSanitizer,
|
||||
:lowercase_element_name => false, :lowercase_attr_name => false,
|
||||
:encoding => @encoding, :tree => @treebuilder })
|
||||
else
|
||||
parsed = XHTMLParser.parse_fragment(html.to_ncr, {:tokenizer => HTMLSanitizer,
|
||||
:lowercase_element_name => false, :lowercase_attr_name => false,
|
||||
:encoding => @encoding, :tree => @treebuilder })
|
||||
end
|
||||
return parsed if @to_tree
|
||||
return parsed.to_s
|
||||
end
|
||||
acceptable_elements = ['a', 'abbr', 'acronym', 'address', 'area', 'b',
|
||||
'big', 'blockquote', 'br', 'button', 'caption', 'center', 'cite',
|
||||
'code', 'col', 'colgroup', 'dd', 'del', 'dfn', 'dir', 'div', 'dl', 'dt',
|
||||
'em', 'fieldset', 'font', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
|
||||
'hr', 'i', 'img', 'input', 'ins', 'kbd', 'label', 'legend', 'li', 'map',
|
||||
'menu', 'ol', 'optgroup', 'option', 'p', 'pre', 'q', 's', 'samp',
|
||||
'select', 'small', 'span', 'strike', 'strong', 'sub', 'sup', 'table',
|
||||
'tbody', 'td', 'textarea', 'tfoot', 'th', 'thead', 'tr', 'tt', 'u',
|
||||
'ul', 'var']
|
||||
|
||||
mathml_elements = ['maction', 'math', 'merror', 'mfrac', 'mi',
|
||||
'mmultiscripts', 'mn', 'mo', 'mover', 'mpadded', 'mphantom',
|
||||
'mprescripts', 'mroot', 'mrow', 'mspace', 'msqrt', 'mstyle', 'msub',
|
||||
'msubsup', 'msup', 'mtable', 'mtd', 'mtext', 'mtr', 'munder',
|
||||
'munderover', 'none']
|
||||
|
||||
svg_elements = ['a', 'animate', 'animateColor', 'animateMotion',
|
||||
'animateTransform', 'circle', 'defs', 'desc', 'ellipse', 'font-face',
|
||||
'font-face-name', 'font-face-src', 'g', 'glyph', 'hkern', 'image',
|
||||
'linearGradient', 'line', 'marker', 'metadata', 'missing-glyph',
|
||||
'mpath', 'path', 'polygon', 'polyline', 'radialGradient', 'rect',
|
||||
'set', 'stop', 'svg', 'switch', 'text', 'title', 'tspan', 'use']
|
||||
|
||||
acceptable_attributes = ['abbr', 'accept', 'accept-charset', 'accesskey',
|
||||
'action', 'align', 'alt', 'axis', 'border', 'cellpadding',
|
||||
'cellspacing', 'char', 'charoff', 'charset', 'checked', 'cite', 'class',
|
||||
'clear', 'cols', 'colspan', 'color', 'compact', 'coords', 'datetime',
|
||||
'dir', 'disabled', 'enctype', 'for', 'frame', 'headers', 'height',
|
||||
'href', 'hreflang', 'hspace', 'id', 'ismap', 'label', 'lang',
|
||||
'longdesc', 'maxlength', 'media', 'method', 'multiple', 'name',
|
||||
'nohref', 'noshade', 'nowrap', 'prompt', 'readonly', 'rel', 'rev',
|
||||
'rows', 'rowspan', 'rules', 'scope', 'selected', 'shape', 'size',
|
||||
'span', 'src', 'start', 'style', 'summary', 'tabindex', 'target', 'title',
|
||||
'type', 'usemap', 'valign', 'value', 'vspace', 'width', 'xml:lang']
|
||||
|
||||
|
||||
mathml_attributes = ['actiontype', 'align', 'columnalign', 'columnalign',
|
||||
'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'depth',
|
||||
'display', 'displaystyle', 'equalcolumns', 'equalrows', 'fence',
|
||||
'fontstyle', 'fontweight', 'frame', 'height', 'linethickness', 'lspace',
|
||||
'mathbackground', 'mathcolor', 'mathvariant', 'mathvariant', 'maxsize',
|
||||
'minsize', 'other', 'rowalign', 'rowalign', 'rowalign', 'rowlines',
|
||||
'rowspacing', 'rowspan', 'rspace', 'scriptlevel', 'selection',
|
||||
'separator', 'stretchy', 'width', 'width', 'xlink:href', 'xlink:show',
|
||||
'xlink:type', 'xmlns', 'xmlns:xlink']
|
||||
|
||||
|
||||
svg_attributes = ['accent-height', 'accumulate', 'additive', 'alphabetic',
|
||||
'arabic-form', 'ascent', 'attributeName', 'attributeType',
|
||||
'baseProfile', 'bbox', 'begin', 'by', 'calcMode', 'cap-height',
|
||||
'class', 'color', 'color-rendering', 'content', 'cx', 'cy', 'd', 'dx',
|
||||
'dy', 'descent', 'display', 'dur', 'end', 'fill', 'fill-rule',
|
||||
'font-family', 'font-size', 'font-stretch', 'font-style', 'font-variant',
|
||||
'font-weight', 'from', 'fx', 'fy', 'g1', 'g2', 'glyph-name',
|
||||
'gradientUnits', 'hanging', 'height', 'horiz-adv-x', 'horiz-origin-x',
|
||||
'id', 'ideographic', 'k', 'keyPoints', 'keySplines', 'keyTimes',
|
||||
'lang', 'marker-end', 'marker-mid', 'marker-start', 'markerHeight',
|
||||
'markerUnits', 'markerWidth', 'mathematical', 'max', 'min', 'name',
|
||||
'offset', 'opacity', 'orient', 'origin', 'overline-position',
|
||||
'overline-thickness', 'panose-1', 'path', 'pathLength', 'points',
|
||||
'preserveAspectRatio', 'r', 'refX', 'refY', 'repeatCount', 'repeatDur',
|
||||
'requiredExtensions', 'requiredFeatures', 'restart', 'rotate', 'rx',
|
||||
'ry', 'slope', 'stemh', 'stemv', 'stop-color', 'stop-opacity',
|
||||
'strikethrough-position', 'strikethrough-thickness', 'stroke',
|
||||
'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap',
|
||||
'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity',
|
||||
'stroke-width', 'systemLanguage', 'target',
|
||||
'text-anchor', 'to', 'transform', 'type', 'u1', 'u2',
|
||||
'underline-position', 'underline-thickness', 'unicode',
|
||||
'unicode-range', 'units-per-em', 'values', 'version', 'viewBox',
|
||||
'visibility', 'width', 'widths', 'x', 'x-height', 'x1', 'x2',
|
||||
'xlink:actuate', 'xlink:arcrole', 'xlink:href', 'xlink:role',
|
||||
'xlink:show', 'xlink:title', 'xlink:type', 'xml:base', 'xml:lang',
|
||||
'xml:space', 'xmlns', 'xmlns:xlink', 'y', 'y1', 'y2', 'zoomAndPan']
|
||||
|
||||
attr_val_is_uri = ['href', 'src', 'cite', 'action', 'longdesc', 'xlink:href']
|
||||
|
||||
# Sanitize a string, parsed using XHTML parsing rules. Reparse the result to
|
||||
# ensure well-formedness.
|
||||
#
|
||||
# :call-seq:
|
||||
# safe_sanitize_xhtml(string) -> string
|
||||
#
|
||||
# Unless otherwise specified, the string is assumed to be utf-8 encoded.
|
||||
#
|
||||
# The string returned is utf-8 encoded. If you want, you can use iconv to convert it to some other encoding.
|
||||
# (REXML trees are always utf-8 encoded.)
|
||||
def safe_sanitize_xhtml(html, options = {})
|
||||
options[:to_tree] = false
|
||||
sanitized = sanitize_xhtml(html, options)
|
||||
doc = REXML::Document.new("<div xmlns='http://www.w3.org/1999/xhtml'>#{sanitized}</div>")
|
||||
sanitized = doc.to_s.gsub(/\A<div xmlns='http:\/\/www.w3.org\/1999\/xhtml'>(.*)<\/div>\Z/m, '\1')
|
||||
rescue REXML::ParseException
|
||||
sanitized = sanitized.escapeHTML
|
||||
end
|
||||
acceptable_css_properties = ['azimuth', 'background-color',
|
||||
'border-bottom-color', 'border-collapse', 'border-color',
|
||||
'border-left-color', 'border-right-color', 'border-top-color', 'clear',
|
||||
'color', 'cursor', 'direction', 'display', 'elevation', 'float', 'font',
|
||||
'font-family', 'font-size', 'font-style', 'font-variant', 'font-weight',
|
||||
'height', 'letter-spacing', 'line-height', 'overflow', 'pause',
|
||||
'pause-after', 'pause-before', 'pitch', 'pitch-range', 'richness',
|
||||
'speak', 'speak-header', 'speak-numeral', 'speak-punctuation',
|
||||
'speech-rate', 'stress', 'text-align', 'text-decoration', 'text-indent',
|
||||
'unicode-bidi', 'vertical-align', 'voice-family', 'volume',
|
||||
'white-space', 'width']
|
||||
|
||||
# Sanitize a string, parsed using HTML parsing rules.
|
||||
#
|
||||
# :call-seq:
|
||||
# sanitize_html( string ) -> string
|
||||
# sanitize_html( string, {:encoding => 'iso-8859-1', :to_tree => true} ) -> REXML::Document
|
||||
#
|
||||
# Unless otherwise specified, the string is assumed to be utf-8 encoded.
|
||||
# By default, the output is a string. But, optionally, you can return a REXML tree.
|
||||
#
|
||||
# The string returned is utf-8 encoded. If you want, you can use iconv to convert it to some other encoding.
|
||||
# (REXML trees are always utf-8 encoded.)
|
||||
def sanitize_html(html, options = {})
|
||||
@encoding = 'utf-8'
|
||||
@treebuilder = TreeBuilders::REXML::TreeBuilder
|
||||
@to_tree = false
|
||||
options.each do |name, value|
|
||||
next unless %w(encoding treebuilder to_tree).include? name.to_s
|
||||
if name.to_s == 'treebuilder'
|
||||
@treebuilder = HTML5lib::TreeBuilders.get_tree_builder(value)
|
||||
else
|
||||
instance_variable_set("@#{name}", value)
|
||||
end
|
||||
end
|
||||
if @encoding == 'utf-8'
|
||||
parsed = HTMLParser.parse_fragment(html.to_utf8, {:tokenizer => HTMLSanitizer,
|
||||
:encoding => @encoding, :tree => @treebuilder })
|
||||
else
|
||||
parsed = HTMLParser.parse_fragment(html.to_ncr, {:tokenizer => HTMLSanitizer,
|
||||
:encoding => @encoding, :tree => @treebuilder })
|
||||
end
|
||||
return parsed if @to_tree
|
||||
return parsed.to_s
|
||||
end
|
||||
acceptable_css_keywords = ['auto', 'aqua', 'black', 'block', 'blue',
|
||||
'bold', 'both', 'bottom', 'brown', 'center', 'collapse', 'dashed',
|
||||
'dotted', 'fuchsia', 'gray', 'green', '!important', 'italic', 'left',
|
||||
'lime', 'maroon', 'medium', 'none', 'navy', 'normal', 'nowrap', 'olive',
|
||||
'pointer', 'purple', 'red', 'right', 'solid', 'silver', 'teal', 'top',
|
||||
'transparent', 'underline', 'white', 'yellow']
|
||||
|
||||
# Sanitize a REXML tree. The output is a string.
|
||||
#
|
||||
# :call-seq:
|
||||
# sanitize_rexml(tree) -> string
|
||||
#
|
||||
def sanitize_rexml(tree)
|
||||
tokens = TreeWalkers.get_tree_walker('rexml2').new(tree)
|
||||
XHTMLSerializer.serialize(tokens, {:encoding=>'utf-8',
|
||||
:space_before_trailing_solidus => true,
|
||||
:inject_meta_charset => false,
|
||||
:sanitize => true})
|
||||
end
|
||||
end
|
||||
acceptable_svg_properties = [ 'fill', 'fill-opacity', 'fill-rule',
|
||||
'stroke', 'stroke-width', 'stroke-linecap', 'stroke-linejoin',
|
||||
'stroke-opacity']
|
||||
|
||||
require 'rexml/element'
|
||||
module REXML #:nodoc:
|
||||
class Element
|
||||
acceptable_protocols = [ 'ed2k', 'ftp', 'http', 'https', 'irc',
|
||||
'mailto', 'news', 'gopher', 'nntp', 'telnet', 'webcal',
|
||||
'xmpp', 'callto', 'feed', 'urn', 'aim', 'rsync', 'tag',
|
||||
'ssh', 'sftp', 'rtsp', 'afs' ]
|
||||
|
||||
# Convert XHTML+MathML Named Entities in a REXML::Element to Numeric Character References
|
||||
#
|
||||
# :call-seq:
|
||||
# tree.to_ncr -> REXML::Element
|
||||
#
|
||||
# REXML, typically, converts NCRs to utf-8 characters, which is what you'll see when you
|
||||
# access the resulting REXML document.
|
||||
#
|
||||
# Note that this method needs to traverse the entire tree, converting text nodes and attributes
|
||||
# for each element. This can be SLOW. It will often be faster to serialize to a string and then
|
||||
# use String.to_ncr instead.
|
||||
#
|
||||
def to_ncr
|
||||
self.each_element { |el|
|
||||
el.texts.each_index {|i|
|
||||
el.texts[i].value = el.texts[i].to_s.to_ncr
|
||||
}
|
||||
el.attributes.each { |name,val|
|
||||
el.attributes[name] = val.to_ncr
|
||||
}
|
||||
el.to_ncr if el.has_elements?
|
||||
}
|
||||
return self
|
||||
end
|
||||
|
||||
# Convert XHTML+MathML Named Entities in a REXML::Element to UTF-8
|
||||
#
|
||||
# :call-seq:
|
||||
# tree.to_utf8 -> REXML::Element
|
||||
#
|
||||
# Note that this method needs to traverse the entire tree, converting text nodes and attributes
|
||||
# for each element. This can be SLOW. It will often be faster to serialize to a string and then
|
||||
# use String.to_utf8 instead.
|
||||
#
|
||||
def to_utf8
|
||||
self.each_element { |el|
|
||||
el.texts.each_index {|i|
|
||||
el.texts[i].value = el.texts[i].to_s.to_utf8
|
||||
}
|
||||
el.attributes.each { |name,val|
|
||||
el.attributes[name] = val.to_utf8
|
||||
}
|
||||
el.to_utf8 if el.has_elements?
|
||||
}
|
||||
return self
|
||||
end
|
||||
ALLOWED_ELEMENTS = acceptable_elements + mathml_elements + svg_elements unless defined?(ALLOWED_ELEMENTS)
|
||||
ALLOWED_ATTRIBUTES = acceptable_attributes + mathml_attributes + svg_attributes unless defined?(ALLOWED_ATTRIBUTES)
|
||||
ALLOWED_CSS_PROPERTIES = acceptable_css_properties unless defined?(ALLOWED_CSS_PROPERTIES)
|
||||
ALLOWED_CSS_KEYWORDS = acceptable_css_keywords unless defined?(ALLOWED_CSS_KEYWORDS)
|
||||
ALLOWED_SVG_PROPERTIES = acceptable_svg_properties unless defined?(ALLOWED_SVG_PROPERTIES)
|
||||
ALLOWED_PROTOCOLS = acceptable_protocols unless defined?(ALLOWED_PROTOCOLS)
|
||||
ATTR_VAL_IS_URI = attr_val_is_uri unless defined?(ATTR_VAL_IS_URI)
|
||||
|
||||
end
|
||||
end
|
||||
# Sanitize the +html+, escaping all elements not in ALLOWED_ELEMENTS, and stripping out all
|
||||
# attributes not in ALLOWED_ATTRIBUTES. Style attributes are parsed, and a restricted set,
|
||||
# specified by ALLOWED_CSS_PROPERTIES and ALLOWED_CSS_KEYWORDS, are allowed through.
|
||||
# attributes in ATTR_VAL_IS_URI are scanned, and only URI schemes specified in
|
||||
# ALLOWED_PROTOCOLS are allowed.
|
||||
# You can adjust what gets sanitized, by defining these constant arrays before this Module is loaded.
|
||||
#
|
||||
# sanitize_html('<script> do_nasty_stuff() </script>')
|
||||
# => <script> do_nasty_stuff() </script>
|
||||
# sanitize_html('<a href="javascript: sucker();">Click here for $100</a>')
|
||||
# => <a>Click here for $100</a>
|
||||
def sanitize_html(html)
|
||||
if html.index("<")
|
||||
tokenizer = HTML::Tokenizer.new(html)
|
||||
new_text = ""
|
||||
|
||||
module HTML5 #:nodoc: all
|
||||
module TreeWalkers
|
||||
|
||||
private
|
||||
|
||||
class << self
|
||||
def [](name)
|
||||
case name.to_s.downcase
|
||||
when 'rexml'
|
||||
require 'html5/treewalkers/rexml'
|
||||
REXML::TreeWalker
|
||||
when 'rexml2'
|
||||
REXML2::TreeWalker
|
||||
else
|
||||
raise "Unknown TreeWalker #{name}"
|
||||
end
|
||||
end
|
||||
|
||||
alias :get_tree_walker :[]
|
||||
end
|
||||
|
||||
module REXML2
|
||||
class TreeWalker < HTML5::TreeWalkers::NonRecursiveTreeWalker
|
||||
|
||||
private
|
||||
|
||||
def node_details(node)
|
||||
case node
|
||||
when ::REXML::Document
|
||||
[:DOCUMENT]
|
||||
when ::REXML::Element
|
||||
if !node.name
|
||||
[:DOCUMENT_FRAGMENT]
|
||||
else
|
||||
[:ELEMENT, node.name,
|
||||
node.attributes.map {|name,value| [name,value.to_utf8]},
|
||||
node.has_elements? || node.has_text?]
|
||||
while token = tokenizer.next
|
||||
node = XHTML::Node.parse(nil, 0, 0, token, false)
|
||||
new_text << case node.tag?
|
||||
when true
|
||||
if ALLOWED_ELEMENTS.include?(node.name)
|
||||
if node.closing != :close
|
||||
node.attributes.delete_if { |attr,v| !ALLOWED_ATTRIBUTES.include?(attr) }
|
||||
ATTR_VAL_IS_URI.each do |attr|
|
||||
val_unescaped = CGI.unescapeHTML(node.attributes[attr].to_s).gsub(/[\000-\040\177\s]+|\302*[\200-\240]/,'').downcase
|
||||
if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ and !ALLOWED_PROTOCOLS.include?(val_unescaped.split(':')[0])
|
||||
node.attributes.delete attr
|
||||
end
|
||||
end
|
||||
if node.attributes['style']
|
||||
node.attributes['style'] = sanitize_css(node.attributes['style'])
|
||||
end
|
||||
end
|
||||
node.to_s
|
||||
else
|
||||
node.to_s.gsub(/</, "<")
|
||||
end
|
||||
else
|
||||
node.to_s.gsub(/</, "<")
|
||||
end
|
||||
when ::REXML::Text
|
||||
[:TEXT, node.value.to_utf8]
|
||||
when ::REXML::Comment
|
||||
[:COMMENT, node.string]
|
||||
when ::REXML::DocType
|
||||
[:DOCTYPE, node.name, node.public, node.system]
|
||||
when ::REXML::XMLDecl
|
||||
[nil]
|
||||
else
|
||||
[:UNKNOWN, node.class.inspect]
|
||||
end
|
||||
end
|
||||
|
||||
def first_child(node)
|
||||
node.children.first
|
||||
end
|
||||
|
||||
def next_sibling(node)
|
||||
node.next_sibling
|
||||
end
|
||||
|
||||
def parent(node)
|
||||
node.parent
|
||||
html = new_text
|
||||
end
|
||||
html
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sanitize_css(style)
|
||||
# disallow urls
|
||||
style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ')
|
||||
|
||||
# gauntlet
|
||||
if style !~ /^([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*$/
|
||||
style = ''
|
||||
return style
|
||||
end
|
||||
if style !~ /^(\s*[-\w]+\s*:\s*[^:;]*(;|$))*$/
|
||||
style = ''
|
||||
return style
|
||||
end
|
||||
|
||||
clean = []
|
||||
style.scan(/([-\w]+)\s*:\s*([^:;]*)/) do |prop,val|
|
||||
if ALLOWED_CSS_PROPERTIES.include?(prop.downcase)
|
||||
clean << prop + ': ' + val + ';'
|
||||
elsif ['background','border','margin','padding'].include?(prop.split('-')[0].downcase)
|
||||
goodval = true
|
||||
val.split().each do |keyword|
|
||||
if !ALLOWED_CSS_KEYWORDS.include?(keyword) and
|
||||
keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/
|
||||
goodval = false
|
||||
end
|
||||
end
|
||||
if goodval
|
||||
clean << prop + ': ' + val + ';'
|
||||
end
|
||||
elsif ALLOWED_SVG_PROPERTIES.include?(prop.downcase)
|
||||
clean << prop + ': ' + val + ';'
|
||||
end
|
||||
end
|
||||
|
||||
style = clean.join(' ')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,189 +1,187 @@
|
|||
#!/usr/bin/env ruby
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
|
||||
require File.expand_path(File.join(File.dirname(__FILE__), '/../test_helper'))
|
||||
require 'sanitize'
|
||||
require 'json'
|
||||
|
||||
|
||||
class SanitizeTest < Test::Unit::TestCase
|
||||
|
||||
include Sanitize
|
||||
|
||||
def setup
|
||||
|
||||
end
|
||||
|
||||
def do_sanitize_xhtml stream
|
||||
safe_sanitize_xhtml(stream)
|
||||
end
|
||||
|
||||
def check_sanitization(input, htmloutput, xhtmloutput, rexmloutput)
|
||||
assert_equal htmloutput, do_sanitize_xhtml(input)
|
||||
end
|
||||
|
||||
def rexml_doc(string)
|
||||
REXML::Document.new(
|
||||
"<div xmlns='http://www.w3.org/1999/xhtml'>#{string}</div>")
|
||||
end
|
||||
|
||||
def my_rex(string)
|
||||
sanitize_rexml(rexml_doc(string.to_utf8)).gsub(/\A<div xmlns="http:\/\/www.w3.org\/1999\/xhtml">(.*)<\/div>\Z/m, '\1')
|
||||
end
|
||||
|
||||
def test_sanitize_named_entities
|
||||
input = '<p>Greek &phis; φ, double-struck 𝔸, numeric 𝔸 ⁗, uppercase ™ <</p>'
|
||||
output = "<p>Greek \317\225 \317\206, double-struck \360\235\224\270, numeric \360\235\224\270 \342\201\227, uppercase \342\204\242 <</p>"
|
||||
output2 = "<p>Greek \317\225 \317\206, double-struck \360\235\224\270, numeric 𝔸 ⁗, uppercase \342\204\242 <</p>"
|
||||
assert_equal(output, sanitize_xhtml(input))
|
||||
assert_equal(output, sanitize_html(input))
|
||||
assert_equal(output, my_rex(input))
|
||||
assert_equal(output2, input.to_utf8)
|
||||
end
|
||||
|
||||
def test_sanitize_malformed_utf8
|
||||
input = "<p>\357elephant & \302ivory</p>"
|
||||
output = "<p>\357\277\275elephant & \357\277\275ivory</p>"
|
||||
check_sanitization(input, output, output, output)
|
||||
end
|
||||
|
||||
Sanitizer::ALLOWED_ELEMENTS.each do |tag_name|
|
||||
Sanitize::ALLOWED_ELEMENTS.each do |tag_name|
|
||||
define_method "test_should_allow_#{tag_name}_tag" do
|
||||
input = "<#{tag_name} title='1'>foo <bad>bar</bad> baz</#{tag_name}>"
|
||||
htmloutput = "<#{tag_name.downcase} title='1'>foo <bad>bar</bad> baz</#{tag_name.downcase}>"
|
||||
xhtmloutput = "<#{tag_name} title='1'>foo <bad>bar</bad> baz</#{tag_name}>"
|
||||
rexmloutput = xhtmloutput
|
||||
|
||||
if %w[caption colgroup optgroup option tbody td tfoot th thead tr].include?(tag_name)
|
||||
htmloutput = "foo <bad>bar</bad> baz"
|
||||
xhtmloutput = htmloutput
|
||||
elsif tag_name == 'col'
|
||||
htmloutput = "foo <bad>bar</bad> baz"
|
||||
xhtmloutput = htmloutput
|
||||
rexmloutput = "<col title='1' />"
|
||||
elsif tag_name == 'table'
|
||||
htmloutput = "foo <bad>bar</bad>baz<table title='1'> </table>"
|
||||
xhtmloutput = htmloutput
|
||||
elsif tag_name == 'image'
|
||||
htmloutput = "<img title='1'/>foo <bad>bar</bad> baz"
|
||||
xhtmloutput = htmloutput
|
||||
rexmloutput = "<image title='1'>foo <bad>bar</bad> baz</image>"
|
||||
elsif VOID_ELEMENTS.include?(tag_name)
|
||||
htmloutput = "<#{tag_name} title='1'/>foo <bad>bar</bad> baz"
|
||||
xhtmloutput = htmloutput
|
||||
htmloutput += '<br/>' if tag_name == 'br'
|
||||
rexmloutput = "<#{tag_name} title='1' />"
|
||||
end
|
||||
check_sanitization(input, xhtmloutput, xhtmloutput, rexmloutput)
|
||||
assert_equal "<#{tag_name} title=\"1\">foo <bad>bar</bad> baz</#{tag_name}>",
|
||||
sanitize_html("<#{tag_name} title='1'>foo <bad>bar</bad> baz</#{tag_name}>")
|
||||
end
|
||||
end
|
||||
|
||||
Sanitizer::ALLOWED_ELEMENTS.each do |tag_name|
|
||||
Sanitize::ALLOWED_ELEMENTS.each do |tag_name|
|
||||
define_method "test_should_forbid_#{tag_name.upcase}_tag" do
|
||||
input = "<#{tag_name.upcase} title='1'>foo <bad>bar</bad> baz</#{tag_name.upcase}>"
|
||||
output = "<#{tag_name.upcase} title=\"1\">foo <bad>bar</bad> baz</#{tag_name.upcase}>"
|
||||
xhtmloutput = "<#{tag_name.upcase} title='1'>foo <bad>bar</bad> baz</#{tag_name.upcase}>"
|
||||
check_sanitization(input, output, xhtmloutput, output)
|
||||
assert_equal "<#{tag_name.upcase} title=\"1\">foo <bad>bar</bad> baz</#{tag_name.upcase}>",
|
||||
sanitize_html("<#{tag_name.upcase} title='1'>foo <bad>bar</bad> baz</#{tag_name.upcase}>")
|
||||
end
|
||||
end
|
||||
|
||||
Sanitizer::ALLOWED_ATTRIBUTES.each do |attribute_name|
|
||||
next if attribute_name == 'style' || attribute_name.include?(':')
|
||||
define_method "test_should_allow_#{attribute_name}_attribute" do
|
||||
input = "<p #{attribute_name}='foo'>foo <bad>bar</bad> baz</p>"
|
||||
output = "<p #{attribute_name}='foo'>foo <bad>bar</bad> baz</p>"
|
||||
htmloutput = "<p #{attribute_name.downcase}='foo'>foo <bad>bar</bad> baz</p>"
|
||||
check_sanitization(input, output, output, output)
|
||||
Sanitize::ALLOWED_ATTRIBUTES.each do |attribute_name|
|
||||
if attribute_name != 'style'
|
||||
define_method "test_should_allow_#{attribute_name}_attribute" do
|
||||
assert_equal "<p #{attribute_name}=\"foo\">foo <bad>bar</bad> baz</p>",
|
||||
sanitize_html("<p #{attribute_name}='foo'>foo <bad>bar</bad> baz</p>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Sanitizer::ALLOWED_ATTRIBUTES.each do |attribute_name|
|
||||
Sanitize::ALLOWED_ATTRIBUTES.each do |attribute_name|
|
||||
define_method "test_should_forbid_#{attribute_name.upcase}_attribute" do
|
||||
input = "<p #{attribute_name.upcase}='display: none;'>foo <bad>bar</bad> baz</p>"
|
||||
output = "<p>foo <bad>bar</bad> baz</p>"
|
||||
check_sanitization(input, output, output, output)
|
||||
assert_equal "<p>foo <bad>bar</bad> baz</p>",
|
||||
sanitize_html("<p #{attribute_name.upcase}='display: none;'>foo <bad>bar</bad> baz</p>")
|
||||
end
|
||||
end
|
||||
|
||||
Sanitizer::ALLOWED_PROTOCOLS.each do |protocol|
|
||||
Sanitize::ALLOWED_PROTOCOLS.each do |protocol|
|
||||
define_method "test_should_allow_#{protocol}_uris" do
|
||||
input = %(<a href="#{protocol}">foo</a>)
|
||||
output = "<a href='#{protocol}'>foo</a>"
|
||||
check_sanitization(input, output, output, output)
|
||||
assert_equal "<a href=\"#{protocol}\">foo</a>",
|
||||
sanitize_html(%(<a href="#{protocol}">foo</a>))
|
||||
end
|
||||
end
|
||||
|
||||
Sanitizer::ALLOWED_PROTOCOLS.each do |protocol|
|
||||
Sanitize::ALLOWED_PROTOCOLS.each do |protocol|
|
||||
define_method "test_should_allow_uppercase_#{protocol}_uris" do
|
||||
input = %(<a href="#{protocol.upcase}">foo</a>)
|
||||
output = "<a href='#{protocol.upcase}'>foo</a>"
|
||||
check_sanitization(input, output, output, output)
|
||||
assert_equal "<a href=\"#{protocol.upcase}\">foo</a>",
|
||||
sanitize_html(%(<a href="#{protocol.upcase}">foo</a>))
|
||||
end
|
||||
end
|
||||
|
||||
Sanitizer::SVG_ALLOW_LOCAL_HREF.each do |tag_name|
|
||||
next unless Sanitizer::ALLOWED_ELEMENTS.include?(tag_name)
|
||||
define_method "test_#{tag_name}_should_allow_local_href_with_ns_decl" do
|
||||
input = %(<#{tag_name} xlink:href="#foo" xmlns:xlink='http://www.w3.org/1999/xlink'/>)
|
||||
output = "<#{tag_name.downcase} xlink:href='#foo' xmlns:xlink='http://www.w3.org/1999/xlink'/>"
|
||||
xhtmloutput = "<#{tag_name} xlink:href='#foo' xmlns:xlink='http://www.w3.org/1999/xlink'/>"
|
||||
check_sanitization(input, xhtmloutput, xhtmloutput, xhtmloutput)
|
||||
end
|
||||
def test_should_allow_anchors
|
||||
assert_equal "<a href=\"foo\"><script>baz</script></a>",
|
||||
sanitize_html("<a href='foo' onclick='bar'><script>baz</script></a>")
|
||||
end
|
||||
|
||||
define_method "test_#{tag_name}_should_allow_local_href_with_newline_and_ns_decl" do
|
||||
input = %(<#{tag_name} xlink:href="\n#foo" xmlns:xlink='http://www.w3.org/1999/xlink'/>)
|
||||
output = "<#{tag_name.downcase} xlink:href='\n#foo' xmlns:xlink='http://www.w3.org/1999/xlink'/>"
|
||||
xhtmloutput = "<#{tag_name} xlink:href='\n#foo' xmlns:xlink='http://www.w3.org/1999/xlink'/>"
|
||||
check_sanitization(input, xhtmloutput, xhtmloutput, xhtmloutput)
|
||||
end
|
||||
# RFC 3986, sec 4.2
|
||||
def test_allow_colons_in_path_component
|
||||
assert_equal "<a href=\"./this:that\">foo</a>",
|
||||
sanitize_html("<a href=\"./this:that\">foo</a>")
|
||||
end
|
||||
|
||||
define_method "test_#{tag_name}_should_forbid_local_href_without_ns_decl" do
|
||||
input = %(<#{tag_name} xlink:href="#foo"/>)
|
||||
output = "<#{tag_name.downcase} xlink:href='#foo'/>"
|
||||
xhtmloutput = "<#{tag_name} xlink:href='#foo'></#{tag_name}>"
|
||||
check_sanitization(input, xhtmloutput, xhtmloutput, xhtmloutput)
|
||||
end
|
||||
|
||||
define_method "test_#{tag_name}_should_forbid_local_href_with_newline_without_ns_decl" do
|
||||
input = %(<#{tag_name} xlink:href="\n#foo"/>)
|
||||
output = "<#{tag_name.downcase} xlink:href='\n#foo'/>"
|
||||
xhtmloutput = "<#{tag_name} xlink:href='\n#foo'></#{tag_name}>"
|
||||
check_sanitization(input, xhtmloutput, xhtmloutput, xhtmloutput)
|
||||
end
|
||||
|
||||
define_method "test_#{tag_name}_should_forbid_nonlocal_href_with_ns_decl" do
|
||||
input = %(<#{tag_name} xlink:href="http://bad.com/foo" xmlns:xlink='http://www.w3.org/1999/xlink'/>)
|
||||
output = "<#{tag_name.downcase} xmlns:xlink='http://www.w3.org/1999/xlink'/>"
|
||||
xhtmloutput = "<#{tag_name} xmlns:xlink='http://www.w3.org/1999/xlink'/>"
|
||||
check_sanitization(input, xhtmloutput, xhtmloutput, xhtmloutput)
|
||||
end
|
||||
|
||||
define_method "test_#{tag_name}_should_forbid_nonlocal_href_with_newline_and_ns_decl" do
|
||||
input = %(<#{tag_name} xlink:href="\nhttp://bad.com/foo" xmlns:xlink='http://www.w3.org/1999/xlink'/>)
|
||||
output = "<#{tag_name.downcase} xmlns:xlink='http://www.w3.org/1999/xlink'/>"
|
||||
xhtmloutput = "<#{tag_name} xmlns:xlink='http://www.w3.org/1999/xlink'/>"
|
||||
check_sanitization(input, xhtmloutput, xhtmloutput, xhtmloutput)
|
||||
%w(src width height alt).each do |img_attr|
|
||||
define_method "test_should_allow_image_#{img_attr}_attribute" do
|
||||
assert_equal "<img #{img_attr}=\"foo\" />",
|
||||
sanitize_html("<img #{img_attr}='foo' onclick='bar' />")
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_handle_astral_plane_characters
|
||||
input = "<p>𝒵 𝔸</p>"
|
||||
output = "<p>\360\235\222\265 \360\235\224\270</p>"
|
||||
check_sanitization(input, output, output, output)
|
||||
def test_should_handle_non_html
|
||||
assert_equal 'abc', sanitize_html("abc")
|
||||
end
|
||||
|
||||
input = "<p><tspan>\360\235\224\270</tspan> a</p>"
|
||||
output = "<p><tspan>\360\235\224\270</tspan> a</p>"
|
||||
check_sanitization(input, output, output, output)
|
||||
def test_should_handle_blank_text
|
||||
assert_equal '', sanitize_html('')
|
||||
end
|
||||
|
||||
[%w(img src), %w(a href)].each do |(tag, attr)|
|
||||
define_method "test_should_strip_#{attr}_attribute_in_#{tag}_with_bad_protocols" do
|
||||
assert_equal %(<#{tag} title="1">boo</#{tag}>), sanitize_html(%(<#{tag} #{attr}="javascript:XSS" title="1">boo</#{tag}>))
|
||||
end
|
||||
end
|
||||
|
||||
[%w(img src), %w(a href)].each do |(tag, attr)|
|
||||
define_method "test_should_strip_#{attr}_attribute_in_#{tag}_with_bad_protocols_and_whitespace" do
|
||||
assert_equal %(<#{tag} title="1">boo</#{tag}>), sanitize_html(%(<#{tag} #{attr}=" javascript:XSS" title="1">boo</#{tag}>))
|
||||
end
|
||||
end
|
||||
|
||||
[%(<img src="javascript:alert('XSS');" />),
|
||||
%(<img src=javascript:alert('XSS') />),
|
||||
%(<img src="JaVaScRiPt:alert('XSS')" />),
|
||||
%(<img src='javascript:alert("XSS")' />),
|
||||
%(<img src='javascript:alert(String.fromCharCode(88,83,83))' />),
|
||||
%(<img src='javascript:alert('XSS')' />),
|
||||
%(<img src='javascript:alert('XSS')' />),
|
||||
%(<img src='javascript:alert('XSS')' />),
|
||||
%(<img src="jav\tascript:alert('XSS');" />),
|
||||
%(<img src="jav	ascript:alert('XSS');" />),
|
||||
%(<img src="jav
ascript:alert('XSS');" />),
|
||||
%(<img src="jav
ascript:alert('XSS');" />),
|
||||
%(<img src="  javascript:alert('XSS');" />),
|
||||
%(<img src=" javascript:alert('XSS');" />),
|
||||
%(<img src=" javascript:alert('XSS');" />)].each_with_index do |img_hack, i|
|
||||
define_method "test_should_not_fall_for_xss_image_hack_#{i}" do
|
||||
assert_equal "<img />", sanitize_html(img_hack)
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_sanitize_tag_broken_up_by_null
|
||||
assert_equal "<scr>alert(\"XSS\")</scr>", sanitize_html(%(<scr\0ipt>alert(\"XSS\")</scr\0ipt>))
|
||||
end
|
||||
|
||||
JSON::parse(open(File.expand_path(File.join(File.dirname(__FILE__), '/../sanitizer.dat'))).read).each do |test|
|
||||
define_method "test_#{test['name']}" do
|
||||
check_sanitization(
|
||||
test['input'],
|
||||
test['output'],
|
||||
test['xhtml'] || test['output'],
|
||||
test['rexml'] || test['output']
|
||||
)
|
||||
end
|
||||
end
|
||||
def test_should_sanitize_invalid_script_tag
|
||||
assert_equal "<script /></script>", sanitize_html(%(<script/XSS SRC="http://ha.ckers.org/xss.js"></script>))
|
||||
end
|
||||
|
||||
def test_should_sanitize_script_tag_with_multiple_open_brackets
|
||||
assert_equal "<<script>alert(\"XSS\");//<</script>", sanitize_html(%(<<script>alert("XSS");//<</script>))
|
||||
assert_equal %(<iframe src="http:" /><), sanitize_html(%(<iframe src=http://ha.ckers.org/scriptlet.html\n<))
|
||||
end
|
||||
|
||||
def test_should_sanitize_unclosed_script
|
||||
assert_equal "<script src=\"http:\" /><b>", sanitize_html(%(<script src=http://ha.ckers.org/xss.js?<b>))
|
||||
end
|
||||
|
||||
def test_should_sanitize_half_open_scripts
|
||||
assert_equal "<img>", sanitize_html(%(<img src="javascript:alert('XSS')"))
|
||||
end
|
||||
|
||||
def test_should_not_fall_for_ridiculous_hack
|
||||
img_hack = %(<img\nsrc\n=\n"\nj\na\nv\na\ns\nc\nr\ni\np\nt\n:\na\nl\ne\nr\nt\n(\n'\nX\nS\nS\n'\n)\n"\n />)
|
||||
assert_equal "<img />", sanitize_html(img_hack)
|
||||
end
|
||||
|
||||
def test_platypus
|
||||
assert_equal %(<a href=\"http://www.ragingplatypus.com/\" style=\"display: block; width: 100%; height: 100%; background-color: black; background-image: ; background-x: center; background-y: center;\">never trust your upstream platypus</a>),
|
||||
sanitize_html(%(<a href="http://www.ragingplatypus.com/" style="display:block; position:absolute; left:0; top:0; width:100%; height:100%; z-index:1; background-color:black; background-image:url(http://www.ragingplatypus.com/i/cam-full.jpg); background-x:center; background-y:center; background-repeat:repeat;">never trust your upstream platypus</a>))
|
||||
end
|
||||
|
||||
def test_xul
|
||||
assert_equal %(<p style="">fubar</p>),
|
||||
sanitize_html(%(<p style="-moz-binding:url('http://ha.ckers.org/xssmoz.xml#xss')">fubar</p>))
|
||||
end
|
||||
|
||||
def test_input_image
|
||||
assert_equal %(<input type="image" />),
|
||||
sanitize_html(%(<input type="image" src="javascript:alert('XSS');" />))
|
||||
end
|
||||
|
||||
def test_non_alpha_non_digit
|
||||
assert_equal "<script /></script>",
|
||||
sanitize_html(%(<script/XSS src="http://ha.ckers.org/xss.js"></script>))
|
||||
assert_equal "<a>foo</a>",
|
||||
sanitize_html('<a onclick!#$%&()*~+-_.,:;?@[/|\]^`=alert("XSS")>foo</a>')
|
||||
assert_equal "<img />",
|
||||
sanitize_html('<img/src="http://ha.ckers.org/xss.js"/>')
|
||||
end
|
||||
|
||||
def test_img_dynsrc_lowsrc
|
||||
assert_equal "<img />",
|
||||
sanitize_html(%(<img dynsrc="javascript:alert('XSS')" />))
|
||||
assert_equal "<img />",
|
||||
sanitize_html(%(<img lowsrc="javascript:alert('XSS')" />))
|
||||
end
|
||||
|
||||
def test_div_background_image_unicode_encoded
|
||||
assert_equal '<div style="">foo</div>',
|
||||
sanitize_html(%(<div style="background-image:\0075\0072\006C\0028'\006a\0061\0076\0061\0073\0063\0072\0069\0070\0074\003a\0061\006c\0065\0072\0074\0028.1027\0058.1053\0053\0027\0029'\0029">foo</div>))
|
||||
end
|
||||
|
||||
def test_div_expression
|
||||
assert_equal '<div style="">foo</div>',
|
||||
sanitize_html(%(<div style="width: expression(alert('XSS'));">foo</div>))
|
||||
end
|
||||
|
||||
def test_img_vbscript
|
||||
assert_equal '<img />',
|
||||
sanitize_html(%(<img src='vbscript:msgbox("XSS")' />))
|
||||
end
|
||||
|
||||
end
|
||||
|
|
21
bundle
21
bundle
|
@ -1,21 +0,0 @@
|
|||
#!/usr/bin/env ruby
|
||||
#
|
||||
# This file was generated by RubyGems.
|
||||
#
|
||||
# The application 'bundler' is installed as part of a gem, and
|
||||
# this file is here to facilitate running it.
|
||||
#
|
||||
|
||||
require 'rubygems'
|
||||
vend = File.join(File.dirname(__FILE__), 'vendor')
|
||||
Gem.use_paths File.join(vend, 'bundle', File.basename(Gem.dir)), (Gem.path + [File.join(vend, 'plugins', 'bundler')])
|
||||
|
||||
version = ">= 0"
|
||||
|
||||
if ARGV.first =~ /^_(.*)_$/ and Gem::Version.correct? $1 then
|
||||
version = $1
|
||||
ARGV.shift
|
||||
end
|
||||
|
||||
gem 'bundler', version
|
||||
load Gem.bin_path('bundler', 'bundle', version)
|
0
cache/.gitignore
vendored
0
cache/.gitignore
vendored
0
components/.gitignore
vendored
0
components/.gitignore
vendored
|
@ -1,7 +1,7 @@
|
|||
# Don't change this file!
|
||||
# Configure your app in config/environment.rb and config/environments/*.rb
|
||||
|
||||
RAILS_ROOT = File.join(File.dirname(__FILE__), '..') unless defined?(RAILS_ROOT)
|
||||
RAILS_ROOT = "#{File.dirname(__FILE__)}/.." unless defined?(RAILS_ROOT)
|
||||
|
||||
module Rails
|
||||
class << self
|
||||
|
@ -21,7 +21,7 @@ module Rails
|
|||
end
|
||||
|
||||
def vendor_rails?
|
||||
File.exist?(File.join(RAILS_ROOT, 'vendor', 'rails'))
|
||||
File.exist?("#{RAILS_ROOT}/vendor/rails")
|
||||
end
|
||||
|
||||
def preinitialize
|
||||
|
@ -29,7 +29,7 @@ module Rails
|
|||
end
|
||||
|
||||
def preinitializer_path
|
||||
File.join(RAILS_ROOT, 'config', 'preinitializer.rb')
|
||||
"#{RAILS_ROOT}/config/preinitializer.rb"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -42,9 +42,8 @@ module Rails
|
|||
|
||||
class VendorBoot < Boot
|
||||
def load_initializer
|
||||
require File.join(RAILS_ROOT, 'vendor', 'rails', 'railties', 'lib', 'initializer')
|
||||
require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
|
||||
Rails::Initializer.run(:install_gem_spec_stubs)
|
||||
Rails::GemDependency.add_frozen_gem_path
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -68,7 +67,7 @@ module Rails
|
|||
|
||||
class << self
|
||||
def rubygems_version
|
||||
Gem::RubyGemsVersion rescue nil
|
||||
Gem::RubyGemsVersion if defined? Gem::RubyGemsVersion
|
||||
end
|
||||
|
||||
def gem_version
|
||||
|
@ -82,15 +81,15 @@ module Rails
|
|||
end
|
||||
|
||||
def load_rubygems
|
||||
min_version = '1.3.6'
|
||||
require 'rubygems'
|
||||
unless rubygems_version >= min_version
|
||||
$stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
|
||||
|
||||
unless rubygems_version >= '0.9.4'
|
||||
$stderr.puts %(Rails requires RubyGems >= 0.9.4 (you have #{rubygems_version}). Please `gem update --system` and try again.)
|
||||
exit 1
|
||||
end
|
||||
|
||||
rescue LoadError
|
||||
$stderr.puts %Q(Rails requires RubyGems >= #{min_version}. Please install RubyGems and try again: http://rubygems.rubyforge.org)
|
||||
$stderr.puts %(Rails requires RubyGems >= 0.9.4. Please install RubyGems and try again: http://rubygems.rubyforge.org)
|
||||
exit 1
|
||||
end
|
||||
|
||||
|
@ -100,25 +99,11 @@ module Rails
|
|||
|
||||
private
|
||||
def read_environment_rb
|
||||
File.read(File.join(RAILS_ROOT, 'config', 'environment.rb'))
|
||||
File.read("#{RAILS_ROOT}/config/environment.rb")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Rails::Boot
|
||||
def run
|
||||
load_initializer
|
||||
|
||||
Rails::Initializer.class_eval do
|
||||
def load_gems
|
||||
@bundler_loaded ||= Bundler.require :default, Rails.env
|
||||
end
|
||||
end
|
||||
|
||||
Rails::Initializer.run(:set_load_path)
|
||||
end
|
||||
end
|
||||
|
||||
# All that for this:
|
||||
Rails.boot!
|
||||
|
|
|
@ -3,9 +3,9 @@
|
|||
####
|
||||
|
||||
# Make sure we are using the latest rexml
|
||||
rexml_versions = ['', File.join(File.dirname(__FILE__), '..', 'vendor', 'plugins', 'rexml', 'lib', '')].collect { |v|
|
||||
`ruby -r "#{v + 'rexml/rexml'}" -e 'p REXML::VERSION'`.split('.').collect {|n| n.to_i} }
|
||||
$:.unshift(File.join(File.dirname(__FILE__), '..', 'vendor', 'plugins', 'rexml', 'lib')) if (rexml_versions[0] <=> rexml_versions[1]) == -1
|
||||
rexml_versions = ['', File.dirname(__FILE__) + '/../vendor/plugins/rexml/lib/'].collect { |v|
|
||||
`ruby -r #{v + 'rexml/rexml'} -e 'p REXML::VERSION'`.split('.').collect {|n| n.to_i} }
|
||||
$:.unshift(File.dirname(__FILE__) + '/../vendor/plugins/rexml/lib') if (rexml_versions[0] <=> rexml_versions[1]) == -1
|
||||
|
||||
require File.join(File.dirname(__FILE__), 'boot')
|
||||
|
||||
|
@ -18,15 +18,15 @@ Rails::Initializer.run do |config|
|
|||
# in a file, for reuse between server restarts. If you want to
|
||||
# change the key, just delete the file, and it will be regenerated
|
||||
# on the next restart. Doing so will invalitate all existing sessions.
|
||||
secret_file = Rails.root.join("secret")
|
||||
secret_file = File.join(RAILS_ROOT, "secret")
|
||||
if File.exist?(secret_file)
|
||||
secret = secret_file.read
|
||||
secret = File.read(secret_file)
|
||||
else
|
||||
secret = ActiveSupport::SecureRandom.hex(64)
|
||||
File.open(secret_file, 'w', 0600) { |f| f.write(secret) }
|
||||
end
|
||||
config.action_controller.session = {
|
||||
:key => "instiki_session",
|
||||
:session_key => "instiki_session",
|
||||
:secret => secret
|
||||
}
|
||||
|
||||
|
@ -58,11 +58,7 @@ end
|
|||
require_dependency 'instiki_errors'
|
||||
|
||||
#require 'jcode'
|
||||
|
||||
# Miscellaneous monkey patches (here be dragons ...)
|
||||
require 'caching_stuff'
|
||||
require 'logging_stuff'
|
||||
require 'rack_stuff'
|
||||
|
||||
#Additional Mime-types
|
||||
mime_types = YAML.load_file(File.join(File.dirname(__FILE__), 'mime_types.yml'))
|
||||
|
|
|
@ -8,7 +8,7 @@ config.cache_classes = true
|
|||
####
|
||||
# This rotates the log file, keeping 25 files, of 1MB each.
|
||||
|
||||
config.action_controller.logger = Logger.new(Rails.root.join('log', "#{RAILS_ENV}.log"), 25, 1024000)
|
||||
config.action_controller.logger = Logger.new("#{RAILS_ROOT}/log/#{RAILS_ENV}.log", 25, 1024000)
|
||||
|
||||
# Unfortunately, the above does not work well under Mongrel, as the default Ruby logger class
|
||||
# does no locking and you will have several processes running, each wanting to write to (and
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
begin
|
||||
require "rubygems"
|
||||
vend = File.join(File.dirname(__FILE__), '..', 'vendor')
|
||||
Gem.use_paths File.join(vend, 'bundle', File.basename(Gem.dir)), (Gem.path + [File.join(vend, 'plugins', 'bundler')])
|
||||
require "bundler"
|
||||
rescue LoadError
|
||||
raise "Could not load the bundler gem. Install it with `gem install bundler`."
|
||||
end
|
||||
|
||||
if Gem::Version.new(Bundler::VERSION) <= Gem::Version.new("0.9.24")
|
||||
raise RuntimeError, "Your bundler version is too old for Rails 2.3." +
|
||||
"Run `gem install bundler` to upgrade."
|
||||
end
|
||||
|
||||
begin
|
||||
# Set up load paths for all bundled gems
|
||||
ENV["BUNDLE_GEMFILE"] = File.join(File.dirname(File.dirname(__FILE__)), 'Gemfile')
|
||||
Bundler.setup
|
||||
rescue Bundler::GemNotFound
|
||||
raise RuntimeError, "Bundler couldn't find some gems." +
|
||||
"Did you run `bundle install`?"
|
||||
end
|
||||
|
|
@ -8,9 +8,6 @@ def connect_to_web(map, generic_path, generic_routing_options)
|
|||
map.connect(generic_path, generic_routing_options)
|
||||
end
|
||||
|
||||
# :id's can be arbitrary junk
|
||||
id_regexp = /.+/
|
||||
|
||||
ActionController::Routing::Routes.draw do |map|
|
||||
map.connect 'create_system', :controller => 'admin', :action => 'create_system'
|
||||
map.connect 'create_web', :controller => 'admin', :action => 'create_web'
|
||||
|
@ -28,14 +25,12 @@ ActionController::Routing::Routes.draw do |map|
|
|||
connect_to_web map, ':web/import/:id', :controller => 'file', :action => 'import'
|
||||
connect_to_web map, ':web/login', :controller => 'wiki', :action => 'login'
|
||||
connect_to_web map, ':web/web_list', :controller => 'wiki', :action => 'web_list'
|
||||
connect_to_web map, ':web/show/diff/:id', :controller => 'wiki', :action => 'show', :mode => 'diff', :requirements => {:id => id_regexp}
|
||||
connect_to_web map, ':web/revision/diff/:id/:rev', :controller => 'wiki', :action => 'revision', :mode => 'diff',
|
||||
:requirements => { :rev => /\d+/, :id => id_regexp}
|
||||
connect_to_web map, ':web/revision/:id/:rev', :controller => 'wiki', :action => 'revision', :requirements => { :rev => /\d+/, :id => id_regexp}
|
||||
connect_to_web map, ':web/source/:id/:rev', :controller => 'wiki', :action => 'source', :requirements => { :rev => /\d+/, :id => id_regexp}
|
||||
connect_to_web map, ':web/show/diff/:id', :controller => 'wiki', :action => 'show', :mode => 'diff'
|
||||
connect_to_web map, ':web/revision/diff/:id/:rev', :controller => 'wiki', :action => 'revision', :mode => 'diff', :requirements => { :rev => /\d*/}
|
||||
connect_to_web map, ':web/revision/:id/:rev', :controller => 'wiki', :action => 'revision', :requirements => { :rev => /\d*/}
|
||||
connect_to_web map, ':web/list/:category', :controller => 'wiki', :action => 'list', :requirements => { :category => /.*/}, :category => nil
|
||||
connect_to_web map, ':web/recently_revised/:category', :controller => 'wiki', :action => 'recently_revised', :requirements => { :category => /.*/}, :category => nil
|
||||
connect_to_web map, ':web/:action/:id', :controller => 'wiki', :requirements => {:id => id_regexp}
|
||||
connect_to_web map, ':web/:action/:id', :controller => 'wiki'
|
||||
connect_to_web map, ':web/:action', :controller => 'wiki'
|
||||
connect_to_web map, ':web', :controller => 'wiki', :action => 'index'
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
require "rubygems"
|
||||
vend = File.join(File.dirname(__FILE__), '..', 'vendor')
|
||||
Gem.use_paths File.join(vend, 'bundle', File.basename(Gem.dir)), (Gem.path + [File.join(vend, 'plugins', 'bundler')])
|
|
@ -1,13 +0,0 @@
|
|||
class ModifyTextTypes < ActiveRecord::Migration
|
||||
def self.up
|
||||
unless adapter_name.eql?('PostgreSQL')
|
||||
change_column :revisions, :content, :text, :limit => 16777215
|
||||
end
|
||||
change_column :pages, :name, :string, :limit => 255
|
||||
change_column :webs, :additional_style, :text
|
||||
end
|
||||
|
||||
def self.down
|
||||
raise ActiveRecord::IrreversibleMigration
|
||||
end
|
||||
end
|
|
@ -1,9 +0,0 @@
|
|||
class ModifyReferencedNameType < ActiveRecord::Migration
|
||||
def self.up
|
||||
change_column :wiki_references, :referenced_name, :string, :limit => 255
|
||||
end
|
||||
|
||||
def self.down
|
||||
raise ActiveRecord::IrreversibleMigration
|
||||
end
|
||||
end
|
Binary file not shown.
97
db/schema.rb
97
db/schema.rb
|
@ -1,33 +1,26 @@
|
|||
# This file is auto-generated from the current state of the database. Instead of editing this file,
|
||||
# please use the migrations feature of Active Record to incrementally modify your database, and
|
||||
# This file is autogenerated. Instead of editing this file, please use the
|
||||
# migrations feature of ActiveRecord to incrementally modify your database, and
|
||||
# then regenerate this schema definition.
|
||||
#
|
||||
# Note that this schema.rb definition is the authoritative source for your database schema. If you need
|
||||
# to create the application database on another system, you should be using db:schema:load, not running
|
||||
# all the migrations from scratch. The latter is a flawed and unsustainable approach (the more migrations
|
||||
# you'll amass, the slower it'll run and the greater likelihood for issues).
|
||||
#
|
||||
# It's strongly recommended to check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(:version => 20100101192755) do
|
||||
ActiveRecord::Schema.define(:version => 2) do
|
||||
|
||||
create_table "pages", :force => true do |t|
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.integer "web_id", :default => 0, :null => false
|
||||
t.string "locked_by", :limit => 60
|
||||
t.string "name"
|
||||
t.datetime "locked_at"
|
||||
t.column "created_at", :datetime, :null => false
|
||||
t.column "updated_at", :datetime, :null => false
|
||||
t.column "web_id", :integer, :default => 0, :null => false
|
||||
t.column "locked_by", :string, :limit => 60
|
||||
t.column "name", :string, :limit => 60
|
||||
t.column "locked_at", :datetime
|
||||
end
|
||||
|
||||
create_table "revisions", :force => true do |t|
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.datetime "revised_at", :null => false
|
||||
t.integer "page_id", :default => 0, :null => false
|
||||
t.text "content", :limit => 16777215, :default => "", :null => false
|
||||
t.string "author", :limit => 60
|
||||
t.string "ip", :limit => 60
|
||||
t.column "created_at", :datetime, :null => false
|
||||
t.column "updated_at", :datetime, :null => false
|
||||
t.column "revised_at", :datetime, :null => false
|
||||
t.column "page_id", :integer, :default => 0, :null => false
|
||||
t.column "content", :text, :default => "", :null => false
|
||||
t.column "author", :string, :limit => 60
|
||||
t.column "ip", :string, :limit => 60
|
||||
end
|
||||
|
||||
add_index "revisions", ["author"], :name => "index_revisions_on_author"
|
||||
|
@ -35,51 +28,51 @@ ActiveRecord::Schema.define(:version => 20100101192755) do
|
|||
add_index "revisions", ["page_id"], :name => "index_revisions_on_page_id"
|
||||
|
||||
create_table "sessions", :force => true do |t|
|
||||
t.string "session_id"
|
||||
t.text "data"
|
||||
t.datetime "updated_at"
|
||||
t.column "session_id", :string
|
||||
t.column "data", :text
|
||||
t.column "updated_at", :datetime
|
||||
end
|
||||
|
||||
add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
|
||||
|
||||
create_table "system", :force => true do |t|
|
||||
t.string "password", :limit => 60
|
||||
t.column "password", :string, :limit => 60
|
||||
end
|
||||
|
||||
create_table "webs", :force => true do |t|
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.string "name", :limit => 60, :default => "", :null => false
|
||||
t.string "address", :limit => 60, :default => "", :null => false
|
||||
t.string "password", :limit => 60
|
||||
t.text "additional_style", :limit => 255
|
||||
t.integer "allow_uploads", :default => 1
|
||||
t.integer "published", :default => 0
|
||||
t.integer "count_pages", :default => 0
|
||||
t.string "markup", :limit => 50, :default => "markdownMML"
|
||||
t.string "color", :limit => 6, :default => "008B26"
|
||||
t.integer "max_upload_size", :default => 100
|
||||
t.integer "safe_mode", :default => 0
|
||||
t.integer "brackets_only", :default => 0
|
||||
t.column "created_at", :datetime, :null => false
|
||||
t.column "updated_at", :datetime, :null => false
|
||||
t.column "name", :string, :limit => 60, :default => "", :null => false
|
||||
t.column "address", :string, :limit => 60, :default => "", :null => false
|
||||
t.column "password", :string, :limit => 60
|
||||
t.column "additional_style", :string
|
||||
t.column "allow_uploads", :integer, :default => 1
|
||||
t.column "published", :integer, :default => 0
|
||||
t.column "count_pages", :integer, :default => 0
|
||||
t.column "markup", :string, :limit => 50, :default => "markdownMML"
|
||||
t.column "color", :string, :limit => 6, :default => "008B26"
|
||||
t.column "max_upload_size", :integer, :default => 100
|
||||
t.column "safe_mode", :integer, :default => 0
|
||||
t.column "brackets_only", :integer, :default => 0
|
||||
end
|
||||
|
||||
create_table "wiki_files", :force => true do |t|
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.integer "web_id", :null => false
|
||||
t.string "file_name", :null => false
|
||||
t.string "description", :null => false
|
||||
t.column "created_at", :datetime, :null => false
|
||||
t.column "updated_at", :datetime, :null => false
|
||||
t.column "web_id", :integer, :null => false
|
||||
t.column "file_name", :string, :null => false
|
||||
t.column "description", :string, :null => false
|
||||
end
|
||||
|
||||
create_table "wiki_references", :force => true do |t|
|
||||
t.datetime "created_at", :null => false
|
||||
t.datetime "updated_at", :null => false
|
||||
t.integer "page_id", :default => 0, :null => false
|
||||
t.string "referenced_name", :default => "", :null => false
|
||||
t.string "link_type", :limit => 1, :default => "", :null => false
|
||||
t.column "created_at", :datetime, :null => false
|
||||
t.column "updated_at", :datetime, :null => false
|
||||
t.column "page_id", :integer, :default => 0, :null => false
|
||||
t.column "referenced_name", :string, :limit => 60, :default => "", :null => false
|
||||
t.column "link_type", :string, :limit => 1, :default => "", :null => false
|
||||
end
|
||||
|
||||
add_index "wiki_references", ["page_id"], :name => "index_wiki_references_on_page_id"
|
||||
add_index "wiki_references", ["referenced_name"], :name => "index_wiki_references_on_referenced_name"
|
||||
add_index "wiki_references", ["page_id"], :name => "index_wiki_references_on_page_id"
|
||||
|
||||
end
|
||||
|
|
0
dump/fixtures/.gitignore
vendored
0
dump/fixtures/.gitignore
vendored
|
@ -8,7 +8,7 @@ module CachingStuff
|
|||
module Caching
|
||||
module Fragments
|
||||
def fragment_cache_key(key)
|
||||
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key.merge(:host=>"")).split(":///").last : key.split('/')[1..-1].join('/').gsub(/\?format=.*/,''), :views)
|
||||
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key.merge(:host=>"")).split(":///").last : key.split('/')[1..-1].join('/'), :views)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require 'chunks/chunk'
|
||||
require 'instiki_stringsupport'
|
||||
require 'stringsupport'
|
||||
|
||||
# The category chunk looks for "category: news" on a line by
|
||||
# itself and parses the terms after the ':' as categories.
|
||||
|
@ -17,9 +17,8 @@ class Category < Chunk::Abstract
|
|||
|
||||
def initialize(match_data, content)
|
||||
super(match_data, content)
|
||||
@content = content
|
||||
@hidden = match_data[1]
|
||||
@list = match_data[2].split(',').map { |c| clean = c.purify.strip.escapeHTML; clean if clean != ''}
|
||||
@list = match_data[2].split(',').map { |c| c.to_s.is_utf8? ? c.strip.escapeHTML : nil }
|
||||
@list.compact!
|
||||
@unmask_text = ''
|
||||
if @hidden
|
||||
|
@ -32,7 +31,6 @@ def initialize(match_data, content)
|
|||
|
||||
# TODO move presentation of page metadata to controller/view
|
||||
def url(category)
|
||||
%{<a class="category_link" href="#{@content.url_generator.url_for :web => @content.web.address,
|
||||
:action => 'list', :only_path => true}/#{CGI.escape(category)}">#{category}</a>}
|
||||
%{<a class="category_link" href="../list/#{category}">#{category}</a>}
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ module Chunk
|
|||
|
||||
# Rails's default utf-8 support causes problems here. So, for Chunk::Abstract class, turn off
|
||||
# multibyte character support.
|
||||
$KCODE = 'n' unless ''.respond_to?(:force_encoding)
|
||||
$KCODE = 'n'
|
||||
|
||||
# automatically construct the array of derivatives of Chunk::Abstract
|
||||
@derivatives = []
|
||||
|
@ -45,16 +45,14 @@ module Chunk
|
|||
|
||||
# Find all the chunks of the given type in content
|
||||
# Each time the pattern is matched, create a new
|
||||
# chunk for it, and replace the occurrence of the chunk
|
||||
# chunk for it, and replace the occurance of the chunk
|
||||
# in this content with its mask.
|
||||
def self.apply_to(content)
|
||||
text = content.to_str
|
||||
text.gsub!( self.pattern ) do |match|
|
||||
content.gsub!( self.pattern ) do |match|
|
||||
new_chunk = self.new($~, content)
|
||||
content.add_chunk(new_chunk)
|
||||
new_chunk.mask
|
||||
end
|
||||
content.replace text
|
||||
end
|
||||
end
|
||||
|
||||
# should contain only [a-z0-9]
|
||||
|
@ -63,7 +61,7 @@ module Chunk
|
|||
end
|
||||
|
||||
def unmask
|
||||
@content.replace @content.sub(mask){|s| s.replace @unmask_text}
|
||||
@content.sub!(mask){|s| s.replace @unmask_text}
|
||||
end
|
||||
|
||||
def rendered?
|
||||
|
@ -75,7 +73,7 @@ module Chunk
|
|||
end
|
||||
|
||||
def revert
|
||||
@content.replace @content.sub(mask){|s| s.replace @text}
|
||||
@content.sub!(mask){|s| s.replace @text}
|
||||
# unregister
|
||||
@content.delete_chunk(self)
|
||||
end
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
$: << File.dirname(__FILE__) + "../../lib"
|
||||
|
||||
require_dependency 'chunks/chunk'
|
||||
require 'instiki_stringsupport'
|
||||
require 'stringsupport'
|
||||
require 'maruku'
|
||||
require 'maruku/ext/math'
|
||||
require_dependency 'rdocsupport'
|
||||
require 'redcloth'
|
||||
require 'oldredcloth'
|
||||
|
||||
# The markup engines are Chunks that call the one of RedCloth
|
||||
# or RDoc to convert text. This markup occurs when the chunk is required
|
||||
|
@ -31,7 +30,6 @@ module Engines
|
|||
|
||||
class Textile < AbstractEngine
|
||||
def mask
|
||||
@content.as_utf8
|
||||
redcloth = RedCloth.new(@content, [:hard_breaks] + @content.options[:engine_opts])
|
||||
redcloth.filter_html = false
|
||||
redcloth.no_span_caps = false
|
||||
|
@ -41,16 +39,15 @@ module Engines
|
|||
|
||||
class Markdown < AbstractEngine
|
||||
def mask
|
||||
text = @content.as_utf8.to_str.delete("\r").to_utf8
|
||||
# If the request is for S5, call Maruku accordingly (without math)
|
||||
if @content.options[:mode] == :s5
|
||||
my_content = Maruku.new(text,
|
||||
my_content = Maruku.new(@content.delete("\r").to_utf8,
|
||||
{:math_enabled => false, :content_only => true,
|
||||
:author => @content.options[:engine_opts][:author],
|
||||
:title => @content.options[:engine_opts][:title]})
|
||||
@content.options[:renderer].s5_theme = my_content.s5_theme
|
||||
else
|
||||
html = Maruku.new(text, {:math_enabled => false}).to_html
|
||||
html = Maruku.new(@content.delete("\r").to_utf8, {:math_enabled => false}).to_html
|
||||
html.gsub(/\A<div class="maruku_wrapper_div">\n?(.*?)\n?<\/div>\Z/m, '\1')
|
||||
end
|
||||
|
||||
|
@ -59,10 +56,9 @@ module Engines
|
|||
|
||||
class MarkdownMML < AbstractEngine
|
||||
def mask
|
||||
text = @content.as_utf8.to_str.delete("\r").to_utf8
|
||||
# If the request is for S5, call Maruku accordingly
|
||||
if @content.options[:mode] == :s5
|
||||
my_content = Maruku.new(text,
|
||||
my_content = Maruku.new(@content.delete("\r").to_utf8,
|
||||
{:math_enabled => true,
|
||||
:math_numbered => ['\\[','\\begin{equation}'],
|
||||
:content_only => true,
|
||||
|
@ -71,11 +67,9 @@ module Engines
|
|||
@content.options[:renderer].s5_theme = my_content.s5_theme
|
||||
my_content.to_s5
|
||||
else
|
||||
(t = Time.now; nil)
|
||||
html = Maruku.new(text,
|
||||
html = Maruku.new(@content.delete("\r").to_utf8,
|
||||
{:math_enabled => true,
|
||||
:math_numbered => ['\\[','\\begin{equation}']}).to_html
|
||||
(ApplicationController.logger.info("Maruku took " + (Time.now-t).to_s + " seconds."); nil)
|
||||
html.gsub(/\A<div class="maruku_wrapper_div">\n?(.*?)\n?<\/div>\Z/m, '\1')
|
||||
end
|
||||
end
|
||||
|
@ -83,31 +77,30 @@ module Engines
|
|||
|
||||
class MarkdownPNG < AbstractEngine
|
||||
def mask
|
||||
text = @content.as_utf8.to_str.delete("\r").to_utf8
|
||||
# If the request is for S5, call Maruku accordingly
|
||||
if @content.options[:mode] == :s5
|
||||
my_content = Maruku.new(text,
|
||||
my_content = Maruku.new(@content.delete("\r").to_utf8,
|
||||
{:math_enabled => true,
|
||||
:math_numbered => ['\\[','\\begin{equation}'],
|
||||
:html_math_output_mathml => false,
|
||||
:html_math_output_png => true,
|
||||
:html_png_engine => 'blahtex',
|
||||
:html_png_dir => @content.web.files_path.join('pngs').to_s,
|
||||
:html_png_url => @content.options[:png_url],
|
||||
:html_png_dir => @content.web.files_path + '/pngs',
|
||||
:html_png_url => '../files/pngs/',
|
||||
:content_only => true,
|
||||
:author => @content.options[:engine_opts][:author],
|
||||
:title => @content.options[:engine_opts][:title]})
|
||||
@content.options[:renderer].s5_theme = my_content.s5_theme
|
||||
my_content.to_s5
|
||||
else
|
||||
html = Maruku.new(text,
|
||||
html = Maruku.new(@content.delete("\r").to_utf8,
|
||||
{:math_enabled => true,
|
||||
:math_numbered => ['\\[','\\begin{equation}'],
|
||||
:html_math_output_mathml => false,
|
||||
:html_math_output_png => true,
|
||||
:html_png_engine => 'blahtex',
|
||||
:html_png_dir => @content.web.files_path.join('pngs').to_s,
|
||||
:html_png_url => @content.options[:png_url]}).to_html
|
||||
:html_png_dir => @content.web.files_path + '/pngs',
|
||||
:html_png_url => '../files/pngs/'}).to_html
|
||||
html.gsub(/\A<div class="maruku_wrapper_div">\n?(.*?)\n?<\/div>\Z/m, '\1')
|
||||
end
|
||||
end
|
||||
|
@ -115,8 +108,7 @@ module Engines
|
|||
|
||||
class Mixed < AbstractEngine
|
||||
def mask
|
||||
@content.as_utf8
|
||||
redcloth = OldRedCloth.new(@content.to_str, @content.options[:engine_opts])
|
||||
redcloth = RedCloth.new(@content, @content.options[:engine_opts])
|
||||
redcloth.filter_html = false
|
||||
redcloth.no_span_caps = false
|
||||
html = redcloth.to_html
|
||||
|
@ -125,7 +117,7 @@ module Engines
|
|||
|
||||
class RDoc < AbstractEngine
|
||||
def mask
|
||||
html = RDocSupport::RDocFormatter.new(@content.as_utf8.to_str).to_html
|
||||
html = RDocSupport::RDocFormatter.new(@content).to_html
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -36,8 +36,7 @@ class Include < WikiChunk::WikiReference
|
|||
else
|
||||
raise "Unsupported rendering mode #{@mode.inspect}"
|
||||
end
|
||||
# redirects and categories of included pages should not be inherited
|
||||
@content.merge_chunks(included_content.delete_chunks!([Redirect, Category]))
|
||||
@content.merge_chunks(included_content)
|
||||
clear_include_list
|
||||
return included_content.pre_rendered
|
||||
else
|
||||
|
|
|
@ -18,7 +18,7 @@ module Literal
|
|||
# A literal chunk that protects 'code' and 'pre' tags from wiki rendering.
|
||||
class Pre < AbstractLiteral
|
||||
PRE_BLOCKS = "a|pre|code|math"
|
||||
PRE_PATTERN = Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?(>.*?</\1>|/>)', Regexp::MULTILINE)
|
||||
PRE_PATTERN = Regexp.new('<('+PRE_BLOCKS+')\b[^>]*?>.*?</\1>', Regexp::MULTILINE)
|
||||
def self.pattern() PRE_PATTERN end
|
||||
end
|
||||
|
||||
|
@ -30,9 +30,9 @@ module Literal
|
|||
|
||||
# A literal chunk that protects equations from wiki rendering.
|
||||
class Math < AbstractLiteral
|
||||
MATH_START = "(?:\\\\\\[|\\${1,2}|\\\\begin\\{equation\\})"
|
||||
MATH_END = "(?:\\\\\\]|\\${1,2}|\\\\end\\{equation\\})"
|
||||
MATH_PATTERN = Regexp.new( '(' + MATH_START + "(?:\\\\\\$|(?!\\$|\\\\\\]|\\\\end\\{equation\\}).)+?" + MATH_END + ')', Regexp::MULTILINE)
|
||||
MATH_START = '(\${1,2}|' + Regexp.escape('\[') + '|\\begin\{equation\})'
|
||||
MATH_END = '(\${1,2}|' + Regexp.escape('\]') + '|\\end\{equation\})'
|
||||
MATH_PATTERN = Regexp.new(MATH_START + '([^$]|\\\$)+?' + MATH_END, Regexp::MULTILINE)
|
||||
def self.pattern() MATH_PATTERN end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
require 'chunks/chunk'
|
||||
require 'sanitizer'
|
||||
require 'sanitize'
|
||||
|
||||
# This chunks allows certain parts of a wiki page to be hidden from the
|
||||
# rest of the rendering pipeline. It should be run at the beginning
|
||||
|
@ -17,7 +17,7 @@ require 'sanitizer'
|
|||
|
||||
class NoWiki < Chunk::Abstract
|
||||
|
||||
include Sanitizer
|
||||
include Sanitize
|
||||
|
||||
NOWIKI_PATTERN = Regexp.new('<nowiki>(.*?)</nowiki>', Regexp::MULTILINE)
|
||||
def self.pattern() NOWIKI_PATTERN end
|
||||
|
@ -26,7 +26,7 @@ class NoWiki < Chunk::Abstract
|
|||
|
||||
def initialize(match_data, content)
|
||||
super
|
||||
@plain_text = @unmask_text = safe_xhtml_sanitize(match_data[1])
|
||||
@plain_text = @unmask_text = safe_sanitize_xhtml(match_data[1])
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
require 'chunks/chunk'
|
||||
require 'instiki_stringsupport'
|
||||
|
||||
# Contains all the methods for finding and replacing wiki related links.
|
||||
module WikiChunk
|
||||
|
@ -34,8 +33,7 @@ module WikiChunk
|
|||
end
|
||||
|
||||
def self.apply_to(content)
|
||||
text = content.as_utf8.to_str
|
||||
text.gsub!( self.pattern ) do |matched_text|
|
||||
content.gsub!( self.pattern ) do |matched_text|
|
||||
chunk = self.new($~, content)
|
||||
if chunk.textile_url?
|
||||
# do not substitute
|
||||
|
@ -45,7 +43,6 @@ module WikiChunk
|
|||
chunk.mask
|
||||
end
|
||||
end
|
||||
content.replace text
|
||||
end
|
||||
|
||||
def textile_url?
|
||||
|
@ -73,9 +70,7 @@ module WikiChunk
|
|||
attr_reader :escaped_text
|
||||
|
||||
unless defined? WIKI_WORD
|
||||
WIKI_WORD = ''.respond_to?(:force_encoding) ?
|
||||
Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0) :
|
||||
Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, 'u')
|
||||
WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, 'u')
|
||||
end
|
||||
|
||||
def self.pattern
|
||||
|
@ -157,7 +152,6 @@ module WikiChunk
|
|||
if web_match
|
||||
@web_name = normalize_whitespace(web_match[1])
|
||||
@page_name = web_match[2]
|
||||
@link_text = @page_name if @link_text == web_match[0]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,20 +0,0 @@
|
|||
class Logger
|
||||
class LogDevice
|
||||
|
||||
private
|
||||
def shift_log_age
|
||||
#For Passenger, restart the server when rotating log files.
|
||||
FileUtils.touch Rails.root.join("tmp", "restart.txt") if defined?(PhusionPassenger)
|
||||
(@shift_age-3).downto(0) do |i|
|
||||
if FileTest.exist?("#{@filename}.#{i}")
|
||||
File.rename("#{@filename}.#{i}", "#{@filename}.#{i+1}")
|
||||
end
|
||||
end
|
||||
@dev.close
|
||||
File.rename("#{@filename}", "#{@filename}.0")
|
||||
@dev = create_logfile(@filename)
|
||||
return true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
0
lib/native/linux-x86/.gitignore
vendored
0
lib/native/linux-x86/.gitignore
vendored
BIN
lib/native/win32/sqlite3.dll
Normal file
BIN
lib/native/win32/sqlite3.dll
Normal file
Binary file not shown.
41
lib/node.rb
41
lib/node.rb
|
@ -77,9 +77,9 @@ module XHTML #:nodoc:
|
|||
|
||||
# Return a textual representation of the node.
|
||||
def to_s
|
||||
s = []
|
||||
s = ""
|
||||
@children.each { |child| s << child.to_s }
|
||||
s.join
|
||||
s
|
||||
end
|
||||
|
||||
# Return false (subclasses must override this to provide specific matching
|
||||
|
@ -150,19 +150,13 @@ module XHTML #:nodoc:
|
|||
end
|
||||
|
||||
if scanner.skip(/!\[CDATA\[/)
|
||||
unless scanner.skip_until(/\]\]>/)
|
||||
if strict
|
||||
raise "expected ]]> (got #{scanner.rest.inspect} for #{content})"
|
||||
else
|
||||
scanner.skip_until(/\Z/)
|
||||
end
|
||||
end
|
||||
|
||||
scanner.scan_until(/\]\]>/)
|
||||
return CDATA.new(parent, line, pos, scanner.pre_match.gsub(/<!\[CDATA\[/, ''))
|
||||
end
|
||||
|
||||
closing = ( scanner.scan(/\//) ? :close : nil )
|
||||
return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:-]+/)
|
||||
name
|
||||
|
||||
unless closing
|
||||
scanner.skip(/\s*/)
|
||||
|
@ -171,18 +165,17 @@ module XHTML #:nodoc:
|
|||
value = true
|
||||
if scanner.scan(/\s*=\s*/)
|
||||
if delim = scanner.scan(/['"]/)
|
||||
v = []
|
||||
value = ""
|
||||
while text = scanner.scan(/[^#{delim}\\]+|./)
|
||||
case text
|
||||
when "\\" then
|
||||
v << text
|
||||
v << scanner.getch
|
||||
value << text
|
||||
value << scanner.getch
|
||||
when delim
|
||||
break
|
||||
else v << text
|
||||
else value << text
|
||||
end
|
||||
end
|
||||
value = v.join
|
||||
else
|
||||
value = scanner.scan(/[^\s>\/]+/)
|
||||
end
|
||||
|
@ -272,7 +265,7 @@ module XHTML #:nodoc:
|
|||
# itself.
|
||||
class CDATA < Text #:nodoc:
|
||||
def to_s
|
||||
"<![CDATA[#{super}]]>"
|
||||
"<![CDATA[#{super}]>"
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -316,20 +309,22 @@ module XHTML #:nodoc:
|
|||
|
||||
# Returns a textual representation of the node
|
||||
def to_s
|
||||
s = ''
|
||||
if @closing == :close
|
||||
"</#{@name}>" unless self.childless?
|
||||
s = "</#{@name}>" unless self.childless?
|
||||
else
|
||||
s = ["<#{@name}"]
|
||||
@attributes.sort.each do |k,v|
|
||||
s << " #{k}"
|
||||
s << "='#{v}'" if String === v
|
||||
s = "<#{@name}"
|
||||
atlist = @attributes.sort
|
||||
atlist.each do |att|
|
||||
s << " #{att[0]}"
|
||||
s << "='#{att[1]}'" if String === att[1]
|
||||
end
|
||||
s << "/" if (@children.empty? && @closing == :self) or self.childless?
|
||||
s << ">"
|
||||
@children.each { |child| s << child.to_s }
|
||||
s << "</#{@name}>" unless @closing == :self or self.childless? or @children.empty?
|
||||
s.join
|
||||
s << "</#{@name}>" if @closing != :self && !@closing.nil? && !@children.empty?
|
||||
end
|
||||
s
|
||||
end
|
||||
|
||||
# If either the node or any of its children meet the given conditions, the
|
||||
|
|
1113
lib/oldredcloth.rb
1113
lib/oldredcloth.rb
File diff suppressed because it is too large
Load diff
|
@ -57,7 +57,7 @@ class PageRenderer
|
|||
|
||||
diffs = ''
|
||||
diff_doc.write(diffs, -1, true, true)
|
||||
diffs.gsub(/\A<div class='xhtmldiff_wrapper'>(.*)<\/div>\Z/m, '\1').html_safe
|
||||
diffs.gsub(/\A<div class='xhtmldiff_wrapper'>(.*)<\/div>\Z/m, '\1')
|
||||
else
|
||||
display_content
|
||||
end
|
||||
|
|
|
@ -1,65 +0,0 @@
|
|||
require 'webrick'
|
||||
require 'stringio'
|
||||
require 'rack/content_length'
|
||||
require 'tempfile'
|
||||
|
||||
module Rack
|
||||
module Handler
|
||||
class WEBrick < ::WEBrick::HTTPServlet::AbstractServlet
|
||||
def self.run(app, options={})
|
||||
options[:BindAddress] = options.delete(:Host) if options[:Host]
|
||||
@server = ::WEBrick::HTTPServer.new(options)
|
||||
@server.mount "/", Rack::Handler::WEBrick, app
|
||||
trap(:INT) { @server.shutdown }
|
||||
trap(:TERM) { @server.shutdown }
|
||||
yield @server if block_given?
|
||||
@server.start
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Rack.release <= "1.2"
|
||||
# The Tempfile bug is fixed in the bundled version of Rack
|
||||
class RewindableInput
|
||||
def make_rewindable
|
||||
# Buffer all data into a tempfile. Since this tempfile is private to this
|
||||
# RewindableInput object, we chmod it so that nobody else can read or write
|
||||
# it. On POSIX filesystems we also unlink the file so that it doesn't
|
||||
# even have a file entry on the filesystem anymore, though we can still
|
||||
# access it because we have the file handle open.
|
||||
@rewindable_io = Tempfile.new('RackRewindableInput')
|
||||
@rewindable_io.chmod(0000)
|
||||
@rewindable_io.set_encoding(Encoding::BINARY) if @rewindable_io.respond_to?(:set_encoding)
|
||||
@rewindable_io.binmode
|
||||
if filesystem_has_posix_semantics? && !tempfile_unlink_contains_bug?
|
||||
@rewindable_io.unlink
|
||||
@unlinked = true
|
||||
end
|
||||
|
||||
buffer = ""
|
||||
while @io.read(1024 * 4, buffer)
|
||||
entire_buffer_written_out = false
|
||||
while !entire_buffer_written_out
|
||||
written = @rewindable_io.write(buffer)
|
||||
entire_buffer_written_out = written == Rack::Utils.bytesize(buffer)
|
||||
if !entire_buffer_written_out
|
||||
buffer.slice!(0 .. written - 1)
|
||||
end
|
||||
end
|
||||
end
|
||||
@rewindable_io.rewind
|
||||
end
|
||||
|
||||
def tempfile_unlink_contains_bug?
|
||||
# The tempfile library as included in Ruby 1.9.1-p152 and later
|
||||
# contains a bug: unlinking an open Tempfile object also closes
|
||||
# it, which breaks our expected POSIX semantics. This problem
|
||||
# has been fixed in Ruby 1.9.2, but the Ruby team chose not to
|
||||
# include the bug fix in later versions of the 1.9.1 series.
|
||||
ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : "ruby"
|
||||
ruby_engine == "ruby" && RUBY_VERSION == "1.9.1" && RUBY_PATCHLEVEL >= 152
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -39,7 +39,7 @@ class RDocMarkup < SM::SimpleMarkup
|
|||
end
|
||||
|
||||
def convert(text, handler)
|
||||
super.sub(/^\n{0,1}<p>\n{0,1}/, '').sub(/\n{0,1}<\/p>\n{0,1}$/, '')
|
||||
super.sub(/^<p>\n/, '').sub(/<\/p>$/, '')
|
||||
end
|
||||
end
|
||||
|
||||
|
|
1113
lib/redcloth.rb
Normal file
1113
lib/redcloth.rb
Normal file
File diff suppressed because it is too large
Load diff
2497
lib/sanitize.rb
Normal file
2497
lib/sanitize.rb
Normal file
File diff suppressed because it is too large
Load diff
239
lib/sanitizer.rb
239
lib/sanitizer.rb
|
@ -7,86 +7,76 @@ module Sanitizer
|
|||
|
||||
require 'action_controller/vendor/html-scanner/html/tokenizer'
|
||||
require 'node'
|
||||
require 'instiki_stringsupport'
|
||||
require 'set'
|
||||
require 'nokogiri'
|
||||
require 'stringsupport'
|
||||
|
||||
acceptable_elements = Set.new %w[a abbr acronym address area article aside
|
||||
audio b big blockquote br button canvas caption center cite code
|
||||
col colgroup command datalist dd del details dfn dialog dir div dl dt
|
||||
em fieldset figcaption figure font footer form h1 h2 h3 h4 h5 h6 header
|
||||
hgroup hr i img input ins kbd label legend li map mark menu meter nav
|
||||
ol optgroup option p pre progress q rp rt ruby s samp section select small
|
||||
source span strike strong sub summary sup table tbody td textarea tfoot
|
||||
th thead time tr tt u ul var video wbr]
|
||||
acceptable_elements = %w[a abbr acronym address area audio b big blockquote br
|
||||
button caption center cite code col colgroup dd del dfn dir div dl dt
|
||||
em fieldset font form h1 h2 h3 h4 h5 h6 hr i img input ins kbd label
|
||||
legend li map menu ol optgroup option p pre q s samp select small span
|
||||
strike strong sub sup table tbody td textarea tfoot th thead tr tt u
|
||||
ul var video]
|
||||
|
||||
mathml_elements = Set.new %w[annotation annotation-xml maction math menclose merror
|
||||
mfrac mfenced mi mmultiscripts mn mo mover mpadded mphantom mprescripts mroot
|
||||
mathml_elements = %w[annotation annotation-xml maction math merror mfrac
|
||||
mfenced mi mmultiscripts mn mo mover mpadded mphantom mprescripts mroot
|
||||
mrow mspace msqrt mstyle msub msubsup msup mtable mtd mtext mtr munder
|
||||
munderover none semantics]
|
||||
|
||||
svg_elements = Set.new %w[a animate animateColor animateMotion animateTransform
|
||||
circle clipPath defs desc ellipse feGaussianBlur filter font-face
|
||||
font-face-name font-face-src foreignObject g glyph hkern linearGradient
|
||||
line marker mask metadata missing-glyph mpath path pattern polygon
|
||||
polyline radialGradient rect set stop svg switch text textPath title tspan use]
|
||||
svg_elements = %w[a animate animateColor animateMotion animateTransform
|
||||
circle clipPath defs desc ellipse font-face font-face-name font-face-src
|
||||
foreignObject g glyph hkern linearGradient line marker metadata
|
||||
missing-glyph mpath path polygon polyline radialGradient rect set
|
||||
stop svg switch text title tspan use]
|
||||
|
||||
acceptable_attributes = Set.new %w[abbr accept accept-charset accesskey action
|
||||
align alt autocomplete axis bgcolor border cellpadding cellspacing char charoff
|
||||
checked cite class clear cols colspan color compact contenteditable contextmenu
|
||||
controls coords datetime dir disabled draggable enctype face for formaction frame
|
||||
headers height high href hreflang hspace icon id ismap label list lang longdesc
|
||||
loop low max maxlength media method min multiple name nohref noshade nowrap open
|
||||
optimumpattern placeholder poster preload pubdate radiogroup readonly rel
|
||||
required rev reversed rows rowspan rules spellcheck scope
|
||||
selected shape size span src start step style summary tabindex target title
|
||||
type usemap valign value vspace width wrap xml:lang]
|
||||
acceptable_attributes = %w[abbr accept accept-charset accesskey action
|
||||
align alt axis border cellpadding cellspacing char charoff charset
|
||||
checked cite class clear cols colspan color compact controls coords datetime
|
||||
dir disabled enctype for frame headers height href hreflang hspace id
|
||||
ismap label lang longdesc loop maxlength media method multiple name nohref
|
||||
noshade nowrap poster prompt readonly rel rev rows rowspan rules scope
|
||||
selected shape size span src start style summary tabindex target title
|
||||
type usemap valign value vspace width xml:lang]
|
||||
|
||||
mathml_attributes = Set.new %w[actiontype align close
|
||||
mathml_attributes = %w[actiontype align close columnalign columnalign
|
||||
columnalign columnlines columnspacing columnspan depth display
|
||||
displaystyle encoding equalcolumns equalrows fence fontstyle fontweight
|
||||
frame height linethickness lspace mathbackground mathcolor mathvariant
|
||||
maxsize minsize notation open other rowalign
|
||||
mathvariant maxsize minsize open other rowalign rowalign rowalign
|
||||
rowlines rowspacing rowspan rspace scriptlevel selection separator
|
||||
separators stretchy width voffset xlink:href xlink:show xlink:type xmlns
|
||||
separators stretchy width width xlink:href xlink:show xlink:type xmlns
|
||||
xmlns:xlink]
|
||||
|
||||
svg_attributes = Set.new %w[accent-height accumulate additive alphabetic
|
||||
svg_attributes = %w[accent-height accumulate additive alphabetic
|
||||
arabic-form ascent attributeName attributeType baseProfile bbox begin
|
||||
by calcMode cap-height class clip-path clip-rule color
|
||||
color-interpolation-filters color-rendering
|
||||
by calcMode cap-height class clip-path clip-rule color color-rendering
|
||||
content cx cy d dx dy descent display dur end fill fill-opacity fill-rule
|
||||
filterRes filterUnits font-family font-size font-stretch font-style
|
||||
font-variant font-weight from fx fy g1 g2 glyph-name gradientUnits
|
||||
hanging height horiz-adv-x horiz-origin-x id ideographic k keyPoints
|
||||
keySplines keyTimes lang marker-end marker-mid marker-start
|
||||
markerHeight markerUnits markerWidth maskContentUnits maskUnits
|
||||
mathematical max method min name offset opacity orient origin
|
||||
overline-position overline-thickness panose-1 path pathLength
|
||||
patternContentUnits patternTransform patternUnits points
|
||||
preserveAspectRatio primitiveUnits r refX refY repeatCount repeatDur
|
||||
requiredExtensions requiredFeatures restart rotate rx ry se:connector
|
||||
se:nonce slope spacing
|
||||
startOffset stdDeviation stemh stemv stop-color stop-opacity
|
||||
strikethrough-position strikethrough-thickness stroke stroke-dasharray
|
||||
stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit
|
||||
stroke-opacity stroke-width systemLanguage target text-anchor
|
||||
to transform type u1 u2 underline-position underline-thickness
|
||||
unicode unicode-range units-per-em values version viewBox
|
||||
visibility width widths x x-height x1 x2 xlink:actuate
|
||||
xlink:arcrole xlink:href xlink:role xlink:show xlink:title xlink:type
|
||||
xml:base xml:lang xml:space xmlns xmlns:xlink xmlns:se y y1 y2 zoomAndPan]
|
||||
font-family font-size font-stretch font-style font-variant font-weight from
|
||||
fx fy g1 g2 glyph-name gradientUnits hanging height horiz-adv-x horiz-origin-x
|
||||
id ideographic k keyPoints keySplines keyTimes lang marker-end
|
||||
marker-mid marker-start markerHeight markerUnits markerWidth
|
||||
mathematical max min name offset opacity orient origin
|
||||
overline-position overline-thickness panose-1 path pathLength points
|
||||
preserveAspectRatio r refX refY repeatCount repeatDur
|
||||
requiredExtensions requiredFeatures restart rotate rx ry slope stemh
|
||||
stemv stop-color stop-opacity strikethrough-position
|
||||
strikethrough-thickness stroke stroke-dasharray stroke-dashoffset
|
||||
stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity
|
||||
stroke-width systemLanguage target text-anchor to transform type u1
|
||||
u2 underline-position underline-thickness unicode unicode-range
|
||||
units-per-em values version viewBox visibility width widths x
|
||||
x-height x1 x2 xlink:actuate xlink:arcrole xlink:href xlink:role
|
||||
xlink:show xlink:title xlink:type xml:base xml:lang xml:space xmlns
|
||||
xmlns:xlink y y1 y2 zoomAndPan]
|
||||
|
||||
attr_val_is_uri = Set.new %w[href src cite action formaction longdesc xlink:href xml:base]
|
||||
attr_val_is_uri = %w[href src cite action longdesc xlink:href xml:base]
|
||||
|
||||
svg_attr_val_allows_ref = Set.new %w[clip-path color-profile cursor fill
|
||||
SVG_ATTR_VAL_ALLOWS_REF = %w[clip-path color-profile cursor fill
|
||||
filter marker marker-start marker-mid marker-end mask stroke]
|
||||
|
||||
svg_allow_local_href = Set.new %w[altGlyph animate animateColor animateMotion
|
||||
SVG_ALLOW_LOCAL_HREF = %w[altGlyph animate animateColor animateMotion
|
||||
animateTransform cursor feImage filter linearGradient pattern
|
||||
radialGradient textpath tref set use]
|
||||
|
||||
acceptable_css_properties = Set.new %w[azimuth background-color
|
||||
acceptable_css_properties = %w[azimuth background-color
|
||||
border-bottom-color border-collapse border-color border-left-color
|
||||
border-right-color border-top-color clear color cursor direction
|
||||
display elevation float font font-family font-size font-style
|
||||
|
@ -96,20 +86,19 @@ module Sanitizer
|
|||
text-align text-decoration text-indent unicode-bidi vertical-align
|
||||
voice-family volume white-space width]
|
||||
|
||||
acceptable_css_keywords = Set.new %w[auto aqua black block blue bold both bottom
|
||||
acceptable_css_keywords = %w[auto aqua black block blue bold both bottom
|
||||
brown center collapse dashed dotted fuchsia gray green !important
|
||||
italic left lime maroon medium none navy normal nowrap olive pointer
|
||||
purple red right solid silver teal top transparent underline white
|
||||
yellow]
|
||||
|
||||
acceptable_svg_properties = Set.new %w[fill fill-opacity fill-rule stroke
|
||||
acceptable_svg_properties = %w[fill fill-opacity fill-rule stroke
|
||||
stroke-width stroke-linecap stroke-linejoin stroke-opacity]
|
||||
|
||||
acceptable_protocols = Set.new %w[ed2k ftp http https irc mailto news gopher nntp
|
||||
acceptable_protocols = %w[ed2k ftp http https irc mailto news gopher nntp
|
||||
telnet webcal xmpp callto feed urn aim rsync tag ssh sftp rtsp afs]
|
||||
|
||||
SHORTHAND_CSS_PROPERTIES = Set.new %w[background border margin padding]
|
||||
VOID_ELEMENTS = Set.new %w[img br hr link meta area base basefont
|
||||
VOID_ELEMENTS = %w[img br hr link meta area base basefont
|
||||
col frame input isindex param]
|
||||
|
||||
ALLOWED_ELEMENTS = acceptable_elements + mathml_elements + svg_elements unless defined?(ALLOWED_ELEMENTS)
|
||||
|
@ -119,76 +108,67 @@ module Sanitizer
|
|||
ALLOWED_SVG_PROPERTIES = acceptable_svg_properties unless defined?(ALLOWED_SVG_PROPERTIES)
|
||||
ALLOWED_PROTOCOLS = acceptable_protocols unless defined?(ALLOWED_PROTOCOLS)
|
||||
ATTR_VAL_IS_URI = attr_val_is_uri unless defined?(ATTR_VAL_IS_URI)
|
||||
SVG_ATTR_VAL_ALLOWS_REF = svg_attr_val_allows_ref unless defined?(SVG_ATTR_VAL_ALLOWS_REF)
|
||||
SVG_ALLOW_LOCAL_HREF = svg_allow_local_href unless defined?(SVG_ALLOW_LOCAL_HREF)
|
||||
|
||||
# Sanitize the +html+, escaping all elements not in ALLOWED_ELEMENTS, and stripping out all
|
||||
# attributes not in ALLOWED_ATTRIBUTES. Style attributes are parsed, and a restricted set,
|
||||
# specified by ALLOWED_CSS_PROPERTIES and ALLOWED_CSS_KEYWORDS, are allowed through.
|
||||
# Attributes in ATTR_VAL_IS_URI are scanned, and only uri schemes specified in
|
||||
# attributes in ATTR_VAL_IS_URI are scanned, and only URI schemes specified in
|
||||
# ALLOWED_PROTOCOLS are allowed.
|
||||
# Certain SVG attributes (SVG_ATTR_VAL_ALLOWS_REF) may take a url as a value. These are restricted to
|
||||
# fragment-id's (in-document references). Certain SVG elements (SVG_ALLOW_LOCAL_HREF) allow href attributes
|
||||
# which, again, are restricted to be fragment-id's.
|
||||
#
|
||||
# You can adjust what gets sanitized, by defining these constant arrays before this Module is loaded.
|
||||
#
|
||||
# xhtml_sanitize('<script> do_nasty_stuff() </script>')
|
||||
# sanitize_html('<script> do_nasty_stuff() </script>')
|
||||
# => <script> do_nasty_stuff() </script>
|
||||
# xhtml_sanitize('<a href="javascript: sucker();">Click here for $100</a>')
|
||||
# sanitize_html('<a href="javascript: sucker();">Click here for $100</a>')
|
||||
# => <a>Click here for $100</a>
|
||||
def xhtml_sanitize(html)
|
||||
return html unless sanitizeable?(html)
|
||||
tokenizer = HTML::Tokenizer.new(html.to_utf8)
|
||||
results = []
|
||||
if html.index("<")
|
||||
tokenizer = HTML::Tokenizer.new(html.to_utf8)
|
||||
new_text = ""
|
||||
|
||||
while token = tokenizer.next
|
||||
node = XHTML::Node.parse(nil, 0, 0, token, false)
|
||||
results << case node.tag?
|
||||
when true
|
||||
if ALLOWED_ELEMENTS.include?(node.name)
|
||||
process_attributes_for node
|
||||
node.to_s
|
||||
while token = tokenizer.next
|
||||
node = XHTML::Node.parse(nil, 0, 0, token, false)
|
||||
new_text << case node.tag?
|
||||
when true
|
||||
if ALLOWED_ELEMENTS.include?(node.name)
|
||||
if node.attributes
|
||||
node.attributes.delete_if { |attr,v| !ALLOWED_ATTRIBUTES.include?(attr) }
|
||||
ATTR_VAL_IS_URI.each do |attr|
|
||||
val_unescaped = node.attributes[attr].to_s.unescapeHTML.gsub(/`|[\000-\040\177\s]+|\302[\200-\240]/,'').downcase
|
||||
if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ and !ALLOWED_PROTOCOLS.include?(val_unescaped.split(':')[0])
|
||||
node.attributes.delete attr
|
||||
end
|
||||
end
|
||||
SVG_ATTR_VAL_ALLOWS_REF.each do |attr|
|
||||
node.attributes[attr] = node.attributes[attr].to_s.gsub(/url\s*\(\s*[^#\s][^)]+?\)/m, ' ') if node.attributes[attr]
|
||||
end
|
||||
if SVG_ALLOW_LOCAL_HREF.include?(node.name) && node.attributes['xlink:href'] && node.attributes['xlink:href'] =~ /^\s*[^#\s].*/m
|
||||
node.attributes.delete 'xlink:href'
|
||||
end
|
||||
if node.attributes['style']
|
||||
node.attributes['style'] = sanitize_css(node.attributes['style'])
|
||||
end
|
||||
node.attributes.each do |attr,val|
|
||||
if String === val
|
||||
node.attributes[attr] = val.unescapeHTML.escapeHTML
|
||||
else
|
||||
node.attributes.delete attr
|
||||
end
|
||||
end
|
||||
end
|
||||
node.to_s
|
||||
else
|
||||
node.to_s.gsub(/</, "<").gsub(/>/, ">")
|
||||
end
|
||||
else
|
||||
node.to_s.gsub(/</, "<").gsub(/>/, ">")
|
||||
end
|
||||
else
|
||||
node.to_s.unescapeHTML.escapeHTML
|
||||
node.to_s.unescapeHTML.escapeHTML
|
||||
end
|
||||
end
|
||||
|
||||
html = new_text
|
||||
end
|
||||
|
||||
results.join
|
||||
html
|
||||
end
|
||||
|
||||
def sanitizeable?(text)
|
||||
!(text.nil? || text.empty? || !text.index("<"))
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def process_attributes_for(node)
|
||||
return unless node.attributes
|
||||
node.attributes.each do |attr,val|
|
||||
if String === val && ALLOWED_ATTRIBUTES.include?(attr)
|
||||
val = val.unescapeHTML.escapeHTML
|
||||
else
|
||||
node.attributes.delete attr; next
|
||||
end
|
||||
if attr == 'xlink:href' && SVG_ALLOW_LOCAL_HREF.include?(node.name) && val =~ /^\s*[^#\s]/m
|
||||
node.attributes.delete attr; next
|
||||
end
|
||||
if ATTR_VAL_IS_URI.include?(attr)
|
||||
val_unescaped = val.unescapeHTML.as_bytes.gsub(/`|[\000-\040\177\s]+|\302[\200-\240]/,'').downcase
|
||||
if val_unescaped =~ /^[a-z0-9][-+.a-z0-9]*:/ && !ALLOWED_PROTOCOLS.include?(val_unescaped.split(':')[0])
|
||||
node.attributes.delete attr; next
|
||||
end
|
||||
end
|
||||
val = val.to_s.gsub(/url\s*\(\s*[^#\s][^)]+?\)/mi, ' ') if SVG_ATTR_VAL_ALLOWS_REF.include?(attr)
|
||||
val = sanitize_css(val) if attr == 'style'
|
||||
node.attributes[attr] = val
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def sanitize_css(style)
|
||||
# disallow urls
|
||||
style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ')
|
||||
|
@ -203,7 +183,7 @@ module Sanitizer
|
|||
prop.downcase!
|
||||
if self.class.const_get("ALLOWED_CSS_PROPERTIES").include?(prop)
|
||||
clean << "#{prop}: #{val};"
|
||||
elsif self.class.const_get("SHORTHAND_CSS_PROPERTIES").include?(prop.split('-')[0])
|
||||
elsif %w[background border margin padding].include?(prop.split('-')[0])
|
||||
clean << "#{prop}: #{val};" unless val.split().any? do |keyword|
|
||||
!self.class.const_get("ALLOWED_CSS_KEYWORDS").include?(keyword) and
|
||||
keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/
|
||||
|
@ -213,25 +193,6 @@ module Sanitizer
|
|||
end
|
||||
end
|
||||
|
||||
clean.join(' ')
|
||||
style = clean.join(' ')
|
||||
end
|
||||
|
||||
# Sanitize a string, parsed using XHTML parsing rules. Reparse the result to
|
||||
# ensure well-formedness.
|
||||
#
|
||||
# :call-seq:
|
||||
# safe_sanitize_xhtml(string) -> string
|
||||
#
|
||||
# Unless otherwise specified, the string is assumed to be utf-8 encoded.
|
||||
#
|
||||
# The string returned is utf-8 encoded. If you want, you can use iconv to convert it to some other encoding.
|
||||
# (REXML trees are always utf-8 encoded.)
|
||||
def safe_xhtml_sanitize(html, options = {})
|
||||
sanitized = xhtml_sanitize(html.purify)
|
||||
doc = Nokogiri::XML::Document.parse("<div xmlns='http://www.w3.org/1999/xhtml'>#{sanitized}</div>", nil, (options[:encoding] || 'UTF-8'), 0)
|
||||
sanitized = doc.root.children.to_xml(:indent => (options[:indent] || 2), :save_with => 2 )
|
||||
rescue Nokogiri::XML::SyntaxError
|
||||
sanitized = sanitized.escapeHTML
|
||||
end
|
||||
|
||||
end
|
||||
|
|
2283
lib/stringsupport.rb
Normal file
2283
lib/stringsupport.rb
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,34 +1,15 @@
|
|||
require 'rake'
|
||||
require 'sqlite3'
|
||||
|
||||
desc "This task will perform necessary upgrades to your Instiki installation"
|
||||
task :upgrade_instiki => :environment do
|
||||
ENV['RAILS_ENV'] ||= 'production'
|
||||
puts "Upgrading Instiki in #{ENV['RAILS_ENV']} environment."
|
||||
|
||||
InstikiUpgrade.migrate_db
|
||||
InstikiUpgrade.move_uploaded_files
|
||||
end
|
||||
|
||||
class InstikiUpgrade
|
||||
|
||||
def self.migrate_db
|
||||
ActiveRecord::Base.establish_connection ENV['RAILS_ENV']
|
||||
Rake::Task["db:migrate"].invoke
|
||||
end
|
||||
|
||||
def self.move_uploaded_files
|
||||
Web.all.each do |web|
|
||||
public_path = Rails.root.join("public", web.address)
|
||||
if public_path.exist?
|
||||
webs_path = Rails.root.join("webs", web.address)
|
||||
if webs_path.exist?
|
||||
puts "Warning! The directory #{webs_path} already exists. Skipping."
|
||||
else
|
||||
public_path.rename(webs_path)
|
||||
puts "Moved #{public_path} to #{webs_path}"
|
||||
end
|
||||
task :upgrade_instiki do
|
||||
db = SQLite3::Database.new( "db/production.db.sqlite3" )
|
||||
db.execute( "select * from webs" ) do |row|
|
||||
if File.exists?('public/' + row[4])
|
||||
if File.exists?('webs/' + row[4])
|
||||
print "Warning! The directory webs/#{row[4]} already exists. Skipping.\n"
|
||||
else
|
||||
File.rename('public/' + row[4], 'webs/' + row[4])
|
||||
print "Moved: #{row[4]}\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
require 'instiki_stringsupport'
|
||||
require 'stringsupport'
|
||||
|
||||
class AbstractUrlGenerator
|
||||
|
||||
|
@ -10,8 +10,7 @@ class AbstractUrlGenerator
|
|||
# Create a link for the given page (or file) name and link text based
|
||||
# on the render mode in options and whether the page (file) exists
|
||||
# in the web.
|
||||
def make_link(current_web, asked_name, web, text = nil, options = {})
|
||||
@web = current_web
|
||||
def make_link(asked_name, web, text = nil, options = {})
|
||||
mode = (options[:mode] || :show).to_sym
|
||||
link_type = (options[:link_type] || :show).to_sym
|
||||
|
||||
|
@ -53,10 +52,7 @@ class AbstractUrlGenerator
|
|||
raise "Unknown link type: #{link_type}"
|
||||
end
|
||||
end
|
||||
|
||||
def url_for(hash = {})
|
||||
@controller.url_for hash
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class UrlGenerator < AbstractUrlGenerator
|
||||
|
@ -64,25 +60,24 @@ class UrlGenerator < AbstractUrlGenerator
|
|||
private
|
||||
|
||||
def file_link(mode, name, text, web_address, known_file, description)
|
||||
return bad_filename(name) unless WikiFile.is_valid?(name)
|
||||
case mode
|
||||
when :export
|
||||
if known_file
|
||||
%{<a class="existingWikiWord" title="#{description}" href="files/#{CGI.escape(name)}">#{text}</a>}
|
||||
%{<a class="existingWikiWord" title="#{description}" href="#{CGI.escape(name)}.#{@controller.html_ext}">#{text}</a>}
|
||||
else
|
||||
%{<span class="newWikiWord">#{text}</span>}
|
||||
end
|
||||
when :publish
|
||||
if known_file
|
||||
href = @controller.url_for :controller => 'file', :web => web_address, :action => 'file',
|
||||
:id => name, :only_path => true
|
||||
href = @controller.url_for :controller => 'file', :web => web_address, :action => 'file',
|
||||
:id => name
|
||||
%{<a class="existingWikiWord" title="#{description}" href="#{href}">#{text}</a>}
|
||||
else
|
||||
%{<span class="newWikiWord">#{text}</span>}
|
||||
end
|
||||
else
|
||||
href = @controller.url_for :controller => 'file', :web => web_address, :action => 'file',
|
||||
:id => name, :only_path => true
|
||||
:id => name
|
||||
if known_file
|
||||
%{<a class="existingWikiWord" title="#{description}" href="#{href}">#{text}</a>}
|
||||
else
|
||||
|
@ -92,38 +87,54 @@ class UrlGenerator < AbstractUrlGenerator
|
|||
end
|
||||
|
||||
def page_link(mode, name, text, web_address, known_page)
|
||||
return %{<span class='wikilink-error'><b>Illegal link (target contains a '.'):</b> #{name}</span>} if name.include?('.')
|
||||
case mode
|
||||
when :export
|
||||
if known_page
|
||||
%{<a class="existingWikiWord" href="#{CGI.escape(name)}.#{html_ext}">#{text}</a>}
|
||||
%{<a class="existingWikiWord" href="#{CGI.escape(name)}.#{@controller.html_ext}">#{text}</a>}
|
||||
else
|
||||
%{<span class="newWikiWord">#{text}</span>}
|
||||
end
|
||||
when :publish
|
||||
if known_page
|
||||
wikilink_for(mode, name, text, web_address)
|
||||
href = @controller.url_for :controller => 'wiki', :web => web_address, :action => 'published',
|
||||
:id => name
|
||||
%{<a class="existingWikiWord" href="#{href}">#{text}</a>}
|
||||
else
|
||||
%{<span class="newWikiWord">#{text}</span>}
|
||||
end
|
||||
else
|
||||
when :show
|
||||
if known_page
|
||||
wikilink_for(mode, name, text, web_address)
|
||||
href = @controller.url_for :controller => 'wiki', :web => web_address, :action => 'show',
|
||||
:id => name
|
||||
%{<a class="existingWikiWord" href="#{href}">#{text}</a>}
|
||||
else
|
||||
href = @controller.url_for :controller => 'wiki', :web => web_address, :action => 'new',
|
||||
:id => name, :only_path => true
|
||||
:id => name
|
||||
%{<span class="newWikiWord">#{text}<a href="#{href}">?</a></span>}
|
||||
end
|
||||
else
|
||||
if known_page
|
||||
web = Web.find_by_address(web_address)
|
||||
action = web.published? ? 'published' : 'show'
|
||||
href = @controller.url_for :controller => 'wiki', :web => web_address, :action => action,
|
||||
:id => name
|
||||
%{<a class="existingWikiWord" href="#{href}">#{text}</a>}
|
||||
else
|
||||
href = @controller.url_for :controller => 'wiki', :web => web_address, :action => 'new',
|
||||
:id => name
|
||||
%{<span class="newWikiWord">#{text}<a href="#{href}">?</a></span>}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def pic_link(mode, name, text, web_address, known_pic)
|
||||
return bad_filename(name) unless WikiFile.is_valid?(name)
|
||||
href = @controller.url_for :controller => 'file', :web => web_address, :action => 'file',
|
||||
:id => name, :only_path => true
|
||||
:id => name
|
||||
case mode
|
||||
when :export
|
||||
if known_pic
|
||||
%{<img alt="#{text}" src="files/#{CGI.escape(name)}" />}
|
||||
%{<img alt="#{text}" src="#{CGI.escape(name)}" />}
|
||||
else
|
||||
%{<img alt="#{text}" src="no image" />}
|
||||
end
|
||||
|
@ -143,13 +154,12 @@ class UrlGenerator < AbstractUrlGenerator
|
|||
end
|
||||
|
||||
def media_link(mode, name, text, web_address, known_media, media_type)
|
||||
return bad_filename(name) unless WikiFile.is_valid?(name)
|
||||
href = @controller.url_for :controller => 'file', :web => web_address, :action => 'file',
|
||||
:id => name, :only_path => true
|
||||
:id => name
|
||||
case mode
|
||||
when :export
|
||||
if known_media
|
||||
%{<#{media_type} src="files/#{CGI.escape(name)}" controls="controls">#{text}</#{media_type}>}
|
||||
%{<#{media_type} src="#{CGI.escape(name)}" controls="controls">#{text}</#{media_type}>}
|
||||
else
|
||||
text
|
||||
end
|
||||
|
@ -170,32 +180,12 @@ class UrlGenerator < AbstractUrlGenerator
|
|||
|
||||
def delete_link(mode, name, web_address, known_file)
|
||||
href = @controller.url_for :controller => 'file', :web => web_address,
|
||||
:action => 'delete', :id => name, :only_oath => true
|
||||
:action => 'delete', :id => name
|
||||
if mode == :show and known_file
|
||||
%{<span class="deleteWikiWord"><a href="#{href}">Delete #{name}</a></span>}
|
||||
else
|
||||
%{<span class="deleteWikiWord">[[#{name}:delete]]</span>}
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def bad_filename(name)
|
||||
"<span class='badWikiWord'>[[invalid filename: #{name}]]</span>"
|
||||
end
|
||||
|
||||
def wikilink_for(mode, name, text, web_address)
|
||||
web = Web.find_by_address(web_address)
|
||||
action = web.published? && (web != @web || [:publish, :s5].include?(mode) ) ? 'published' : 'show'
|
||||
href = @controller.url_for :controller => 'wiki', :web => web_address, :action => action,
|
||||
:id => name, :only_path => true
|
||||
title = web == @web ? '' : %{ title="#{web_address}"}
|
||||
%{<a class="existingWikiWord" href="#{href}"#{title}>#{text}</a>}
|
||||
end
|
||||
|
||||
def html_ext
|
||||
@html_ext ||= @controller.method(:html_ext).call
|
||||
# Why method().call ? A Ruby 1.9.2preview1 bug:
|
||||
# http://redmine.ruby-lang.org/issues/show/1802
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -7,8 +7,6 @@ require_dependency 'chunks/wiki'
|
|||
require_dependency 'chunks/literal'
|
||||
require 'chunks/nowiki'
|
||||
require 'sanitizer'
|
||||
require 'instiki_stringsupport'
|
||||
require 'set'
|
||||
|
||||
|
||||
# Wiki content is just a string that can process itself with a chain of
|
||||
|
@ -54,10 +52,10 @@ module ChunkManager
|
|||
def init_chunk_manager
|
||||
@chunks_by_type = Hash.new
|
||||
Chunk::Abstract::derivatives.each{|chunk_type|
|
||||
@chunks_by_type[chunk_type] = Set.new
|
||||
@chunks_by_type[chunk_type] = Array.new
|
||||
}
|
||||
@chunks_by_id = Hash.new
|
||||
@chunks = Set.new
|
||||
@chunks = []
|
||||
@chunk_id = 0
|
||||
end
|
||||
|
||||
|
@ -116,7 +114,7 @@ class WikiContentStub < String
|
|||
end
|
||||
end
|
||||
|
||||
class WikiContent < ActiveSupport::SafeBuffer
|
||||
class WikiContent < String
|
||||
|
||||
include ChunkManager
|
||||
include Sanitizer
|
||||
|
@ -129,7 +127,7 @@ class WikiContent < ActiveSupport::SafeBuffer
|
|||
:mode => :show
|
||||
}.freeze
|
||||
|
||||
attr_reader :web, :options, :revision, :not_rendered, :pre_rendered, :url_generator
|
||||
attr_reader :web, :options, :revision, :not_rendered, :pre_rendered
|
||||
|
||||
# Create a new wiki content string from the given one.
|
||||
# The options are explained at the top of this file.
|
||||
|
@ -144,12 +142,6 @@ class WikiContent < ActiveSupport::SafeBuffer
|
|||
@options[:active_chunks] = (ACTIVE_CHUNKS - [WikiChunk::Word] ) if @web.brackets_only?
|
||||
@options[:hide_chunks] = (HIDE_CHUNKS - [Literal::Math] ) unless
|
||||
[Engines::MarkdownMML, Engines::MarkdownPNG].include?(@options[:engine])
|
||||
if @options[:engine] == Engines::MarkdownPNG
|
||||
@options[:png_url] =
|
||||
@options[:mode] == :export ? 'files/pngs/' :
|
||||
(@url_generator.url_for :controller => 'file', :web => @web.address,
|
||||
:action => 'file', :id => 'pngs', :only_path => true) + '/'
|
||||
end
|
||||
|
||||
@not_rendered = @pre_rendered = nil
|
||||
|
||||
|
@ -163,7 +155,7 @@ class WikiContent < ActiveSupport::SafeBuffer
|
|||
def page_link(web_name, name, text, link_type)
|
||||
web = Web.find_by_name(web_name) || Web.find_by_address(web_name) || @web
|
||||
@options[:link_type] = (link_type || :show)
|
||||
@url_generator.make_link(@web, name, web, text, @options)
|
||||
@url_generator.make_link(name, web, text, @options)
|
||||
end
|
||||
|
||||
def build_chunks
|
||||
|
@ -182,20 +174,10 @@ class WikiContent < ActiveSupport::SafeBuffer
|
|||
@options[:engine].apply_to(copy)
|
||||
|
||||
copy.inside_chunks(@options[:hide_chunks]) do |id|
|
||||
@chunks_by_id[id.to_i].revert if @chunks_by_id[id.to_i]
|
||||
@chunks_by_id[id.to_i].revert
|
||||
end
|
||||
end
|
||||
|
||||
def delete_chunks!(types)
|
||||
types.each do |t|
|
||||
@chunks_by_type[t].each do |c|
|
||||
@pre_rendered.sub!(c.mask, '') if @pre_rendered
|
||||
@chunks.delete(c)
|
||||
end
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def pre_render!
|
||||
unless @pre_rendered
|
||||
@chunks_by_type[Include].each{|chunk| chunk.unmask }
|
||||
|
@ -207,10 +189,8 @@ class WikiContent < ActiveSupport::SafeBuffer
|
|||
def render!
|
||||
pre_render!
|
||||
@options[:engine].apply_to(self)
|
||||
as_utf8
|
||||
# unmask in one go. $~[1] is the chunk id
|
||||
text = self.to_str
|
||||
text.gsub!(MASK_RE[ACTIVE_CHUNKS]) do
|
||||
gsub!(MASK_RE[ACTIVE_CHUNKS]) do
|
||||
chunk = @chunks_by_id[$~[1].to_i]
|
||||
if chunk.nil?
|
||||
# if we match a chunkmask that existed in the original content string
|
||||
|
@ -220,8 +200,7 @@ class WikiContent < ActiveSupport::SafeBuffer
|
|||
chunk.unmask_text
|
||||
end
|
||||
end
|
||||
self.replace xhtml_sanitize(text)
|
||||
self.html_safe
|
||||
self.replace xhtml_sanitize(self)
|
||||
end
|
||||
|
||||
def page_name
|
||||
|
|
|
@ -1,28 +1,24 @@
|
|||
#coding: utf-8
|
||||
# Contains all the methods for finding and replacing wiki words
|
||||
|
||||
require 'instiki_stringsupport'
|
||||
|
||||
module WikiWords
|
||||
# In order of appearance: Latin, greek, cyrillic, armenian
|
||||
I18N_HIGHER_CASE_LETTERS =
|
||||
"ÀÁÂÃÄÅĀĄĂÆÇĆČĈĊĎĐÈÉÊËĒĘĚĔĖĜĞĠĢĤĦÌÍÎÏĪĨĬĮİIJĴĶŁĽĹĻĿÑŃŇŅŊÒÓÔÕÖØŌŐŎŒŔŘŖŚŠŞŜȘŤŢŦȚÙÚÛÜŪŮŰŬŨŲŴŶŸȲÝŹŽŻ" +
|
||||
"ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ" +
|
||||
"ЀЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯѠѢѤѦѨѪѬѮѰѲѴѶѸѺѼѾҀҊҌҎҐҒҔҖҘҚҜҞҠҢҤҦҨҪҬҮҰҲҴҶҸҺҼҾӀӁӃӅӇӉӋӍӐӒӔӖӘӚӜӞӠӢӤӦӨӪӬӮӰӲӴӶӸӺӼӾԀԂԄԆԈԊԌԎԐԒԔԖԘԚԜԞԠԢ" +
|
||||
"ΆΈΉΊΌΎΏѠѢѤѦѨѪѬѮѰѲѴѶѸѺѼѾҀҊҌҎҐҒҔҖҘҚҜҞҠҢҤҦҨҪҬҮҰҲҴҶҸҺҼҾӁӃӅӇӉӋӍӏӐӒӔӖӘӚӜӞӠӢӤӦӨӪӬӮӰӲӴӸЖ" +
|
||||
"ԱԲԳԴԵԶԷԸԹԺԻԼԽԾԿՀՁՂՃՄՅՆՇՈՉՊՋՌՍՎՏՐՑՒՓՔՕՖ"
|
||||
|
||||
I18N_LOWER_CASE_LETTERS =
|
||||
"àáâãäåāąăæçćĉċčďđèéêëēęěĕėƒĝğġģĥħìíîïīĩĭįıijĵķĸłľĺļŀñńňņʼnŋòóôõöøōŏőœŕřŗśŝšşșťţŧțùúûüūůűŭũųŵýÿŷžżźÞþßſð" +
|
||||
"άέήίΰαβγδεζηθικλμνξοπρςστυφχψωϊϋόύώΐ" +
|
||||
"абвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљњћќѝўџѡѣѥѧѩѫѭѯѱѳѵѷѹѻѽѿҁҋҍҏґғҕҗҙқҝҟҡңҥҧҩҫҭүұҳҵҷҹһҽҿӂӄӆӈӊӌӎӏӑӓӕӗәӛӝӟӡӣӥӧөӫӭӯӱӳӵӷӹӻӽӿԁԃԅԇԉԋԍԏԑԓԕԗԙԛԝԟԡԣ" +
|
||||
"աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև"
|
||||
"абвгдежзийклмнопрстуфхцчшщъыьэюяѐёђѓєѕіїјљћќѝўџѡѣѥѧѩѫѭѯѱѳѵѷѹѻѽѿҁҋҌҍҏґғҕҗҙқҝҟҡңҥҧҩҫҭүұҳҵҷҹһҽҿӀӂӄӆӈӊӌӎӑӓӕӗәӛӝӟӡӣӥӧөӫӭӯӱӳӵӹ" +
|
||||
"աբգդեզէըթժիլխծկհձղճմյնշոչպջռսվտրցւփքօֆև"
|
||||
|
||||
WIKI_WORD_PATTERN = '[A-Z' + I18N_HIGHER_CASE_LETTERS + ']+[a-z' + I18N_LOWER_CASE_LETTERS + ']+[A-Z' + I18N_HIGHER_CASE_LETTERS +
|
||||
'][A-Za-z0-9_' + I18N_HIGHER_CASE_LETTERS + I18N_LOWER_CASE_LETTERS + ']+'
|
||||
WIKI_WORD_PATTERN = '[A-Z' + I18N_HIGHER_CASE_LETTERS + ']+[a-z' + I18N_LOWER_CASE_LETTERS + ']+[A-Z' + I18N_HIGHER_CASE_LETTERS + ']\w+'
|
||||
CAMEL_CASED_WORD_BORDER = /([a-z#{I18N_LOWER_CASE_LETTERS}])([A-Z#{I18N_HIGHER_CASE_LETTERS}])/u
|
||||
|
||||
def self.separate(wiki_word)
|
||||
wiki_word.dup.as_utf8.gsub(CAMEL_CASED_WORD_BORDER, '\1 \2')
|
||||
wiki_word.gsub(CAMEL_CASED_WORD_BORDER, '\1 \2')
|
||||
end
|
||||
|
||||
end
|
||||
|
|
202
public/MathJax/COPYING.txt
vendored
202
public/MathJax/COPYING.txt
vendored
|
@ -1,202 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
202
public/MathJax/LICENSE
vendored
202
public/MathJax/LICENSE
vendored
|
@ -1,202 +0,0 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
30
public/MathJax/MathJax.js
vendored
30
public/MathJax/MathJax.js
vendored
File diff suppressed because one or more lines are too long
1
public/MathJax/README-branch.txt
vendored
1
public/MathJax/README-branch.txt
vendored
|
@ -1 +0,0 @@
|
|||
This is release branch v1.1 of MathJax.
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue