r74390 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r74389‎ | r74390 | r74391 >
Date:21:13, 6 October 2010
Author:bharris
Status:resolved (Comments)
Tags:
Comment:
Initial commit of extension.
Modified paths:
  • /trunk/extensions/TradeTrack (added) (history)
  • /trunk/extensions/TradeTrack/LICENSE (added) (history)
  • /trunk/extensions/TradeTrack/README (added) (history)
  • /trunk/extensions/TradeTrack/SpecialTradeTrack.php (added) (history)
  • /trunk/extensions/TradeTrack/TradeTrack.i18n.php (added) (history)
  • /trunk/extensions/TradeTrack/TradeTrack.php (added) (history)
  • /trunk/extensions/TradeTrack/TradeTrack.sql (added) (history)
  • /trunk/extensions/TradeTrack/css (added) (history)
  • /trunk/extensions/TradeTrack/css/TradeTrack.css (added) (history)
  • /trunk/extensions/TradeTrack/images (added) (history)
  • /trunk/extensions/TradeTrack/images/arrow.gif (added) (history)
  • /trunk/extensions/TradeTrack/images/errormark.gif (added) (history)
  • /trunk/extensions/TradeTrack/images/question-hover.gif (added) (history)
  • /trunk/extensions/TradeTrack/images/question.gif (added) (history)
  • /trunk/extensions/TradeTrack/images/tipsy.gif (added) (history)
  • /trunk/extensions/TradeTrack/js (added) (history)
  • /trunk/extensions/TradeTrack/js/TradeTrack.js (added) (history)
  • /trunk/extensions/TradeTrack/js/jquery.NobleCount.js (added) (history)
  • /trunk/extensions/TradeTrack/js/jquery.tipsy.js (added) (history)
  • /trunk/extensions/TradeTrack/templates (added) (history)
  • /trunk/extensions/TradeTrack/templates/TradeTrackEmail.php (added) (history)
  • /trunk/extensions/TradeTrack/templates/TradeTrackScreen.php (added) (history)
  • /trunk/extensions/TradeTrack/templates/TradeTrackScreenDetailsForm.php (added) (history)
  • /trunk/extensions/TradeTrack/templates/TradeTrackScreenNonComAgreement.php (added) (history)
  • /trunk/extensions/TradeTrack/templates/TradeTrackScreenRouting.php (added) (history)
  • /trunk/extensions/TradeTrack/templates/TradeTrackScreenThanks.php (added) (history)

Diff [purge]

Index: trunk/extensions/TradeTrack/LICENSE
@@ -0,0 +1,339 @@
 2+ GNU GENERAL PUBLIC LICENSE
 3+ Version 2, June 1991
 4+
 5+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
 6+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 7+ Everyone is permitted to copy and distribute verbatim copies
 8+ of this license document, but changing it is not allowed.
 9+
 10+ Preamble
 11+
 12+ The licenses for most software are designed to take away your
 13+freedom to share and change it. By contrast, the GNU General Public
 14+License is intended to guarantee your freedom to share and change free
 15+software--to make sure the software is free for all its users. This
 16+General Public License applies to most of the Free Software
 17+Foundation's software and to any other program whose authors commit to
 18+using it. (Some other Free Software Foundation software is covered by
 19+the GNU Lesser General Public License instead.) You can apply it to
 20+your programs, too.
 21+
 22+ When we speak of free software, we are referring to freedom, not
 23+price. Our General Public Licenses are designed to make sure that you
 24+have the freedom to distribute copies of free software (and charge for
 25+this service if you wish), that you receive source code or can get it
 26+if you want it, that you can change the software or use pieces of it
 27+in new free programs; and that you know you can do these things.
 28+
 29+ To protect your rights, we need to make restrictions that forbid
 30+anyone to deny you these rights or to ask you to surrender the rights.
 31+These restrictions translate to certain responsibilities for you if you
 32+distribute copies of the software, or if you modify it.
 33+
 34+ For example, if you distribute copies of such a program, whether
 35+gratis or for a fee, you must give the recipients all the rights that
 36+you have. You must make sure that they, too, receive or can get the
 37+source code. And you must show them these terms so they know their
 38+rights.
 39+
 40+ We protect your rights with two steps: (1) copyright the software, and
 41+(2) offer you this license which gives you legal permission to copy,
 42+distribute and/or modify the software.
 43+
 44+ Also, for each author's protection and ours, we want to make certain
 45+that everyone understands that there is no warranty for this free
 46+software. If the software is modified by someone else and passed on, we
 47+want its recipients to know that what they have is not the original, so
 48+that any problems introduced by others will not reflect on the original
 49+authors' reputations.
 50+
 51+ Finally, any free program is threatened constantly by software
 52+patents. We wish to avoid the danger that redistributors of a free
 53+program will individually obtain patent licenses, in effect making the
 54+program proprietary. To prevent this, we have made it clear that any
 55+patent must be licensed for everyone's free use or not licensed at all.
 56+
 57+ The precise terms and conditions for copying, distribution and
 58+modification follow.
 59+
 60+ GNU GENERAL PUBLIC LICENSE
 61+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
 62+
 63+ 0. This License applies to any program or other work which contains
 64+a notice placed by the copyright holder saying it may be distributed
 65+under the terms of this General Public License. The "Program", below,
 66+refers to any such program or work, and a "work based on the Program"
 67+means either the Program or any derivative work under copyright law:
 68+that is to say, a work containing the Program or a portion of it,
 69+either verbatim or with modifications and/or translated into another
 70+language. (Hereinafter, translation is included without limitation in
 71+the term "modification".) Each licensee is addressed as "you".
 72+
 73+Activities other than copying, distribution and modification are not
 74+covered by this License; they are outside its scope. The act of
 75+running the Program is not restricted, and the output from the Program
 76+is covered only if its contents constitute a work based on the
 77+Program (independent of having been made by running the Program).
 78+Whether that is true depends on what the Program does.
 79+
 80+ 1. You may copy and distribute verbatim copies of the Program's
 81+source code as you receive it, in any medium, provided that you
 82+conspicuously and appropriately publish on each copy an appropriate
 83+copyright notice and disclaimer of warranty; keep intact all the
 84+notices that refer to this License and to the absence of any warranty;
 85+and give any other recipients of the Program a copy of this License
 86+along with the Program.
 87+
 88+You may charge a fee for the physical act of transferring a copy, and
 89+you may at your option offer warranty protection in exchange for a fee.
 90+
 91+ 2. You may modify your copy or copies of the Program or any portion
 92+of it, thus forming a work based on the Program, and copy and
 93+distribute such modifications or work under the terms of Section 1
 94+above, provided that you also meet all of these conditions:
 95+
 96+ a) You must cause the modified files to carry prominent notices
 97+ stating that you changed the files and the date of any change.
 98+
 99+ b) You must cause any work that you distribute or publish, that in
 100+ whole or in part contains or is derived from the Program or any
 101+ part thereof, to be licensed as a whole at no charge to all third
 102+ parties under the terms of this License.
 103+
 104+ c) If the modified program normally reads commands interactively
 105+ when run, you must cause it, when started running for such
 106+ interactive use in the most ordinary way, to print or display an
 107+ announcement including an appropriate copyright notice and a
 108+ notice that there is no warranty (or else, saying that you provide
 109+ a warranty) and that users may redistribute the program under
 110+ these conditions, and telling the user how to view a copy of this
 111+ License. (Exception: if the Program itself is interactive but
 112+ does not normally print such an announcement, your work based on
 113+ the Program is not required to print an announcement.)
 114+
 115+These requirements apply to the modified work as a whole. If
 116+identifiable sections of that work are not derived from the Program,
 117+and can be reasonably considered independent and separate works in
 118+themselves, then this License, and its terms, do not apply to those
 119+sections when you distribute them as separate works. But when you
 120+distribute the same sections as part of a whole which is a work based
 121+on the Program, the distribution of the whole must be on the terms of
 122+this License, whose permissions for other licensees extend to the
 123+entire whole, and thus to each and every part regardless of who wrote it.
 124+
 125+Thus, it is not the intent of this section to claim rights or contest
 126+your rights to work written entirely by you; rather, the intent is to
 127+exercise the right to control the distribution of derivative or
 128+collective works based on the Program.
 129+
 130+In addition, mere aggregation of another work not based on the Program
 131+with the Program (or with a work based on the Program) on a volume of
 132+a storage or distribution medium does not bring the other work under
 133+the scope of this License.
 134+
 135+ 3. You may copy and distribute the Program (or a work based on it,
 136+under Section 2) in object code or executable form under the terms of
 137+Sections 1 and 2 above provided that you also do one of the following:
 138+
 139+ a) Accompany it with the complete corresponding machine-readable
 140+ source code, which must be distributed under the terms of Sections
 141+ 1 and 2 above on a medium customarily used for software interchange; or,
 142+
 143+ b) Accompany it with a written offer, valid for at least three
 144+ years, to give any third party, for a charge no more than your
 145+ cost of physically performing source distribution, a complete
 146+ machine-readable copy of the corresponding source code, to be
 147+ distributed under the terms of Sections 1 and 2 above on a medium
 148+ customarily used for software interchange; or,
 149+
 150+ c) Accompany it with the information you received as to the offer
 151+ to distribute corresponding source code. (This alternative is
 152+ allowed only for noncommercial distribution and only if you
 153+ received the program in object code or executable form with such
 154+ an offer, in accord with Subsection b above.)
 155+
 156+The source code for a work means the preferred form of the work for
 157+making modifications to it. For an executable work, complete source
 158+code means all the source code for all modules it contains, plus any
 159+associated interface definition files, plus the scripts used to
 160+control compilation and installation of the executable. However, as a
 161+special exception, the source code distributed need not include
 162+anything that is normally distributed (in either source or binary
 163+form) with the major components (compiler, kernel, and so on) of the
 164+operating system on which the executable runs, unless that component
 165+itself accompanies the executable.
 166+
 167+If distribution of executable or object code is made by offering
 168+access to copy from a designated place, then offering equivalent
 169+access to copy the source code from the same place counts as
 170+distribution of the source code, even though third parties are not
 171+compelled to copy the source along with the object code.
 172+
 173+ 4. You may not copy, modify, sublicense, or distribute the Program
 174+except as expressly provided under this License. Any attempt
 175+otherwise to copy, modify, sublicense or distribute the Program is
 176+void, and will automatically terminate your rights under this License.
 177+However, parties who have received copies, or rights, from you under
 178+this License will not have their licenses terminated so long as such
 179+parties remain in full compliance.
 180+
 181+ 5. You are not required to accept this License, since you have not
 182+signed it. However, nothing else grants you permission to modify or
 183+distribute the Program or its derivative works. These actions are
 184+prohibited by law if you do not accept this License. Therefore, by
 185+modifying or distributing the Program (or any work based on the
 186+Program), you indicate your acceptance of this License to do so, and
 187+all its terms and conditions for copying, distributing or modifying
 188+the Program or works based on it.
 189+
 190+ 6. Each time you redistribute the Program (or any work based on the
 191+Program), the recipient automatically receives a license from the
 192+original licensor to copy, distribute or modify the Program subject to
 193+these terms and conditions. You may not impose any further
 194+restrictions on the recipients' exercise of the rights granted herein.
 195+You are not responsible for enforcing compliance by third parties to
 196+this License.
 197+
 198+ 7. If, as a consequence of a court judgment or allegation of patent
 199+infringement or for any other reason (not limited to patent issues),
 200+conditions are imposed on you (whether by court order, agreement or
 201+otherwise) that contradict the conditions of this License, they do not
 202+excuse you from the conditions of this License. If you cannot
 203+distribute so as to satisfy simultaneously your obligations under this
 204+License and any other pertinent obligations, then as a consequence you
 205+may not distribute the Program at all. For example, if a patent
 206+license would not permit royalty-free redistribution of the Program by
 207+all those who receive copies directly or indirectly through you, then
 208+the only way you could satisfy both it and this License would be to
 209+refrain entirely from distribution of the Program.
 210+
 211+If any portion of this section is held invalid or unenforceable under
 212+any particular circumstance, the balance of the section is intended to
 213+apply and the section as a whole is intended to apply in other
 214+circumstances.
 215+
 216+It is not the purpose of this section to induce you to infringe any
 217+patents or other property right claims or to contest validity of any
 218+such claims; this section has the sole purpose of protecting the
 219+integrity of the free software distribution system, which is
 220+implemented by public license practices. Many people have made
 221+generous contributions to the wide range of software distributed
 222+through that system in reliance on consistent application of that
 223+system; it is up to the author/donor to decide if he or she is willing
 224+to distribute software through any other system and a licensee cannot
 225+impose that choice.
 226+
 227+This section is intended to make thoroughly clear what is believed to
 228+be a consequence of the rest of this License.
 229+
 230+ 8. If the distribution and/or use of the Program is restricted in
 231+certain countries either by patents or by copyrighted interfaces, the
 232+original copyright holder who places the Program under this License
 233+may add an explicit geographical distribution limitation excluding
 234+those countries, so that distribution is permitted only in or among
 235+countries not thus excluded. In such case, this License incorporates
 236+the limitation as if written in the body of this License.
 237+
 238+ 9. The Free Software Foundation may publish revised and/or new versions
 239+of the General Public License from time to time. Such new versions will
 240+be similar in spirit to the present version, but may differ in detail to
 241+address new problems or concerns.
 242+
 243+Each version is given a distinguishing version number. If the Program
 244+specifies a version number of this License which applies to it and "any
 245+later version", you have the option of following the terms and conditions
 246+either of that version or of any later version published by the Free
 247+Software Foundation. If the Program does not specify a version number of
 248+this License, you may choose any version ever published by the Free Software
 249+Foundation.
 250+
 251+ 10. If you wish to incorporate parts of the Program into other free
 252+programs whose distribution conditions are different, write to the author
 253+to ask for permission. For software which is copyrighted by the Free
 254+Software Foundation, write to the Free Software Foundation; we sometimes
 255+make exceptions for this. Our decision will be guided by the two goals
 256+of preserving the free status of all derivatives of our free software and
 257+of promoting the sharing and reuse of software generally.
 258+
 259+ NO WARRANTY
 260+
 261+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
 262+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
 263+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
 264+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
 265+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 266+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
 267+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
 268+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
 269+REPAIR OR CORRECTION.
 270+
 271+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
 272+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
 273+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
 274+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
 275+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
 276+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
 277+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
 278+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
 279+POSSIBILITY OF SUCH DAMAGES.
 280+
 281+ END OF TERMS AND CONDITIONS
 282+
 283+ How to Apply These Terms to Your New Programs
 284+
 285+ If you develop a new program, and you want it to be of the greatest
 286+possible use to the public, the best way to achieve this is to make it
 287+free software which everyone can redistribute and change under these terms.
 288+
 289+ To do so, attach the following notices to the program. It is safest
 290+to attach them to the start of each source file to most effectively
 291+convey the exclusion of warranty; and each file should have at least
 292+the "copyright" line and a pointer to where the full notice is found.
 293+
 294+ <one line to give the program's name and a brief idea of what it does.>
 295+ Copyright (C) <year> <name of author>
 296+
 297+ This program is free software; you can redistribute it and/or modify
 298+ it under the terms of the GNU General Public License as published by
 299+ the Free Software Foundation; either version 2 of the License, or
 300+ (at your option) any later version.
 301+
 302+ This program is distributed in the hope that it will be useful,
 303+ but WITHOUT ANY WARRANTY; without even the implied warranty of
 304+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 305+ GNU General Public License for more details.
 306+
 307+ You should have received a copy of the GNU General Public License along
 308+ with this program; if not, write to the Free Software Foundation, Inc.,
 309+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 310+
 311+Also add information on how to contact you by electronic and paper mail.
 312+
 313+If the program is interactive, make it output a short notice like this
 314+when it starts in an interactive mode:
 315+
 316+ Gnomovision version 69, Copyright (C) year name of author
 317+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
 318+ This is free software, and you are welcome to redistribute it
 319+ under certain conditions; type `show c' for details.
 320+
 321+The hypothetical commands `show w' and `show c' should show the appropriate
 322+parts of the General Public License. Of course, the commands you use may
 323+be called something other than `show w' and `show c'; they could even be
 324+mouse-clicks or menu items--whatever suits your program.
 325+
 326+You should also get your employer (if you work as a programmer) or your
 327+school, if any, to sign a "copyright disclaimer" for the program, if
 328+necessary. Here is a sample; alter the names:
 329+
 330+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
 331+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
 332+
 333+ <signature of Ty Coon>, 1 April 1989
 334+ Ty Coon, President of Vice
 335+
 336+This General Public License does not permit incorporating your program into
 337+proprietary programs. If your program is a subroutine library, you may
 338+consider it more useful to permit linking proprietary applications with the
 339+library. If this is what you want to do, use the GNU Lesser General
 340+Public License instead of this License.
Index: trunk/extensions/TradeTrack/TradeTrack.sql
@@ -0,0 +1,67 @@
 2+-- Store mapping of i18n key of "trademark" to an ID
 3+CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/tradetrack_trademarks (
 4+ -- Trademark Id
 5+ tt_mark_id int unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT,
 6+ -- Text (i18n key) for rating description
 7+ tt_mark varchar(255) binary NOT NULL
 8+) /*$wgDBTableOptions*/;
 9+
 10+-- Default trademarks
 11+INSERT INTO /*$wgDBprefix*/tradetrack_trademarks (tt_mark) VALUES
 12+ ('wmf'),
 13+ ('wikipedia'),
 14+ ('wiktionary'),
 15+ ('wikiquote'),
 16+ ('wikibooks'),
 17+ ('wikiversity'),
 18+ ('wikispecies'),
 19+ ('wikisource'),
 20+ ('mediawiki'),
 21+ ('wikimediacommons'),
 22+ ('wikimediaincubator'),
 23+ ('wikinews'),
 24+ ('other') /*$wgDBTableOptions*/;
 25+
 26+-- Store request data
 27+CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/tradetrack_requests (
 28+ -- Request Id
 29+ tt_request_id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
 30+ -- This defines the purpose type flag (commercial, non-commercial, media)
 31+ tt_purpose VARCHAR(200) NOT NULL DEFAULT "",
 32+ -- This defines whether or not there is an existing agreement in place
 33+ tt_agreement VARCHAR(200) NOT NULL DEFAULT "",
 34+ -- Store the provided name
 35+ tt_name VARCHAR(200) NOT NULL,
 36+ -- Store the provided email
 37+ tt_email VARCHAR(200) NOT NULL,
 38+ -- Store the provided organization name
 39+ tt_orgname VARCHAR(200) NOT NULL,
 40+ -- Store the value set for "other" if selected
 41+ tt_otherval VARCHAR(200) NULL,
 42+ -- Store the phone number
 43+ tt_phone VARCHAR(200) NOT NULL,
 44+ -- MW Timestamp
 45+ tt_timestamp BINARY(14) NOT NULL DEFAULT '',
 46+ -- This stores the text describing how the marks will be used.
 47+ tt_usage TEXT NOT NULL,
 48+ -- This stores the user's mailing address. It probably doesn't need to be this large.
 49+ tt_mailingaddress TEXT NOT NULL
 50+) /*$wgDBTableOptions*/;
 51+
 52+-- Store individual mark data
 53+CREATE TABLE IF NOT EXISTS /*$wgDBprefix*/tradetrack_mark_requests (
 54+ -- Foreign key to tradetrack_requests.tt_request_id
 55+ tt_request_id INT UNSIGNED NOT NULL,
 56+ -- Foreign key to tradetrack_trademarks.tt_mark_id
 57+ tt_mark_id INT UNSIGNED NOT NULL,
 58+ -- MW Timestamp
 59+ tt_timestamp BINARY(14) NOT NULL DEFAULT '',
 60+
 61+ PRIMARY KEY (tt_request_id, tt_mark_id, tt_timestamp)
 62+) /*$wgDBTableOptions*/;
 63+CREATE INDEX /*i*/tt_mark_connection ON /*_*/tradetrack_mark_requests (tt_request_id);
 64+
 65+
 66+
 67+
 68+
Index: trunk/extensions/TradeTrack/images/errormark.gif
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: trunk/extensions/TradeTrack/images/errormark.gif
___________________________________________________________________
Added: svn:mime-type
169 + application/octet-stream
Index: trunk/extensions/TradeTrack/images/question.gif
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: trunk/extensions/TradeTrack/images/question.gif
___________________________________________________________________
Added: svn:mime-type
270 + application/octet-stream
Index: trunk/extensions/TradeTrack/images/question-hover.gif
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: trunk/extensions/TradeTrack/images/question-hover.gif
___________________________________________________________________
Added: svn:mime-type
371 + application/octet-stream
Index: trunk/extensions/TradeTrack/images/tipsy.gif
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: trunk/extensions/TradeTrack/images/tipsy.gif
___________________________________________________________________
Added: svn:mime-type
472 + application/octet-stream
Index: trunk/extensions/TradeTrack/images/arrow.gif
Cannot display: file marked as a binary type.
svn:mime-type = application/octet-stream
Property changes on: trunk/extensions/TradeTrack/images/arrow.gif
___________________________________________________________________
Added: svn:mime-type
573 + application/octet-stream
Index: trunk/extensions/TradeTrack/TradeTrack.i18n.php
@@ -0,0 +1,147 @@
 2+<?php
 3+$messages = array();
 4+
 5+/** English
 6+ * @author Brandon Harris
 7+ */
 8+$messages['en'] = array(
 9+ 'tradetrack-desc' => 'Trade Track, a tool for tracking Wikimedia trademark requests',
 10+ 'tradetrack-header' => 'Requests to use Wikimedia trademarks',
 11+ 'tradetrack-all-fields-required' => 'All fields are required.',
 12+ 'tradetrack-overview' => "Although the user-generated content of Wikimedia projects are generally licensed through open-source licensing schemes such as Creative Commons, the same is not true of Wikimedia trademarks and word marks. The Wikimedia Foundation owns all Wikimedia trademarks, logos, and word marks associated with Wikimedia projects, and use of that content without the Foundation's permission is prohibited.
 13+ If you or the agency you represent would like to use a word mark or trademarked logo owned by the Wikimedia Foundation, please submit a request using the form below.",
 14+ 'tradetrack-purpose-question' => 'For what purpose will the trademark be used?',
 15+ 'tradetrack-purpose-label-commercial' => 'Commercial',
 16+ 'tradetrack-purpose-expanse-commercial' => '(for-profit)',
 17+ 'tradetrack-purpose-label-noncommercial' => 'Non-commercial',
 18+ 'tradetrack-purpose-expanse-noncommercial' => '(non-profit or educational)',
 19+ 'tradetrack-purpose-label-media' => 'Media',
 20+ 'tradetrack-purpose-expanse-media' => '(incidental use in TV, film, or other media)',
 21+ 'tradetrack-usage-label' => 'Please describe how you will use the trademark',
 22+ 'tradetrack-usage-expanse' => "Will it be used online? As part of a product or service incorporating Wikimedia content? To promote another product? For journalism or commentary?",
 23+ 'tradetrack-logo-which' => "Which word mark or trademarked logo do you want to use?",
 24+ 'tradetrack-which-wmf' => 'Wikimedia Foundation',
 25+ 'tradetrack-which-wikipedia' => 'Wikipedia',
 26+ 'tradetrack-which-wiktionary' => 'Wiktionary',
 27+ 'tradetrack-which-wikiquote' => 'Wikiquote',
 28+ 'tradetrack-which-wikibooks' => 'Wikibooks',
 29+ 'tradetrack-which-wikiversity' => 'Wikiversity',
 30+ 'tradetrack-which-wikispecies' => 'Wikispecies',
 31+ 'tradetrack-which-wikisource' => 'Wikisource',
 32+ 'tradetrack-which-mediawiki' => 'Mediawiki',
 33+ 'tradetrack-which-wikimediacommons' => 'Wikimedia Commons',
 34+ 'tradetrack-which-wikimediaincubator' => 'Wikimedia Incubator',
 35+ 'tradetrack-which-wikinews' => 'Wikinews',
 36+ 'tradetrack-which-other' => 'Other',
 37+ 'tradetrack-about-label-mailingaddress' => 'Mailing address',
 38+ 'tradetrack-about-expanse-mailingaddress' => "Please provide contact information where we can follow up with your request. Remember to include your country!",
 39+ 'tradetrack-about-label-yourname' => 'Your name',
 40+ 'tradetrack-about-label-orgname' => "Your organization's name",
 41+ 'tradetrack-about-expanse-orgname' => 'The name of the entity that will use the Wikimedia trademark.',
 42+ 'tradetrack-about-label-email' => 'Email address',
 43+ 'tradetrack-about-expanse-email' => 'Your primary email address',
 44+ 'tradetrack-about-label-confirmemail' => 'Email address confirmation',
 45+ 'tradetrack-about-expanse-confirmemail' => 'Type your primary email address again',
 46+ 'tradetrack-about-label-phone' => 'Phone number',
 47+ 'tradetrack-statement-label' => 'Statement of Good Faith',
 48+ 'tradetrack-statement-expanse' => "By checking the box below, I hereby swear or affirm under penalty of perjury that I am the authorized representative of the organization or person named above, and that I completed this form accurately, in good faith, and to the best of my ability.",
 49+ 'tradetrack-statement-checkboxlabel' => 'I swear or affirm',
 50+ 'tradetrack-characters-remaining-notice' => 'Characters Remaining',
 51+ 'tradetrack-button-continue' => 'Continue',
 52+ 'tradetrack-button-back' => 'Back',
 53+ 'tradetrack-button-submit' => 'Submit',
 54+ 'tradetrack-nonprofit-preexisting-agreement-question' => 'If you are operating a non-profit organization, do you have a preexisting agreement or contract with the Wikimedia Foundation?',
 55+ 'tradetrack-nonprofit-preexisting-agreement-yes' => 'Yes, I am a non-profit organization with a preexisting relationship with Wikimedia.',
 56+ 'tradetrack-nonprofit-preexisting-agreement-no-unaffilliated' => 'No, I am a non-profit organization unaffiliated with Wikimedia.',
 57+ 'tradetrack-nonprofit-preexisting-agreement-no-mistake' => 'No. And I made a mistake; I am NOT a non-profit organization.',
 58+ 'tradetrack-thanks-header' => 'Thank You',
 59+ 'tradetrack-thanks-text' => 'Thank you for your interest. Your request has been submitted and will be routed to the correct person for review.',
 60+ 'tradetrack-errors-have-happened' => 'One or more errors have occurred. Please correct them below.',
 61+ 'tradetrack-errors-pacman-death' => 'An unknown error has occurred. Please start again.',
 62+ 'tradetrack-errors-no-route' => 'You must select which type of request you are making.',
 63+ 'tradetrack-errors-invalid-route' => 'The type of request supplied is invalid.',
 64+ 'tradetrack-errors-noncom-no-selection' => 'You must select one of the available agreement types.',
 65+ 'tradetrack-errors-noncom-invalid-selection' => 'The agreement type provided is invalid.',
 66+ 'tradetrack-errors-zero-marks' => "You haven't selected any logos or trademarks.",
 67+ 'tradetrack-errors-other-set-but-not-checked' => 'You have entered a value for "other" but did not select it.',
 68+ 'tradetrack-errors-missing-other-value' => 'You selected "other" but did not provide a value for it.',
 69+ 'tradetrack-errors-other-too-long' => 'The value supplied for "other" is too long. The maximum length for this field is $1 characters.',
 70+ 'tradetrack-errors-generic-empty' => 'This field is required.',
 71+ 'tradetrack-errors-generic-too-long' => 'The value supplied for this field is too long. The maximum length allowed is $1 characters.',
 72+ 'tradetrack-errors-emails-do-not-match' => 'The email addresses supplied do not match.',
 73+ 'tradetrack-errors-email-fails-regex' => 'The email address supplied is invalid. Please provide a valid email address.',
 74+ 'tradetrack-errors-no-accept-statement' => 'You must agree to the Statement of Good Faith.',
 75+);
 76+
 77+/** Message documentation (Message documentation)
 78+ * @author Brandon Harris
 79+ */
 80+$messages['qqq'] = array(
 81+ 'tradetrack-desc' => 'Trade Track, a tool for tracking Wikimedia trademark requests',
 82+ 'tradetrack-header' => 'Header for (most) pages',
 83+ 'tradetrack-all-fields-required' => 'Indicates that all form elements are required.',
 84+ 'tradetrack-overview' => 'Describes the purpose of the special page to the user.',
 85+ 'tradetrack-purpose-question' => 'Asks the user for their primary purpose for the trademark usage',
 86+ 'tradetrack-purpose-label-commercial' => 'Term for commercial focused usages',
 87+ 'tradetrack-purpose-expanse-commercial' => 'Provides more information about what constitutes a commercial purpose',
 88+ 'tradetrack-purpose-label-noncommercial' => 'Term for non-commercial focused usages',
 89+ 'tradetrack-purpose-expanse-noncommercial' => 'Provides more information about what constitutes a non-commercial purpose',
 90+ 'tradetrack-purpose-label-media' => 'Term for media-focused usages (e.g., television, movies)',
 91+ 'tradetrack-purpose-expanse-media' => 'Provides more information about what constitutes a media purpose',
 92+ 'tradetrack-usage-label' => 'Asks how the user will use the trademark',
 93+ 'tradetrack-usage-expanse' => "Gives further clarification as to what the tradetrack-usage-label question means",
 94+ 'tradetrack-logo-which' => "Asks which logo or logos the user wishes to use.",
 95+ // These are all trademark terms owned by the foundation.
 96+ 'tradetrack-which-wmf' => 'Wikimedia Foundation',
 97+ 'tradetrack-which-wikipedia' => 'Wikipedia',
 98+ 'tradetrack-which-wiktionary' => 'Wiktionary',
 99+ 'tradetrack-which-wikiquote' => 'Wikiquote',
 100+ 'tradetrack-which-wikibooks' => 'Wikibooks',
 101+ 'tradetrack-which-wikiversity' => 'Wikiversity',
 102+ 'tradetrack-which-wikispecies' => 'Wikispecies',
 103+ 'tradetrack-which-wikisource' => 'Wikisource',
 104+ 'tradetrack-which-mediawiki' => 'Mediawiki',
 105+ 'tradetrack-which-wikimediacommons' => 'Wikimedia Commons',
 106+ 'tradetrack-which-wikimediaincubator' => 'Wikimedia Incubator',
 107+ 'tradetrack-which-wikinews' => 'Wikinews',
 108+ // End terms
 109+ 'tradetrack-which-other' => 'This is a label in case the user wishes to utilize a mark not listed',
 110+ 'tradetrack-about-label-mailingaddress' => 'Label for the mailing address field',
 111+ 'tradetrack-about-expanse-mailingaddress' => "More information about the mailing address field.",
 112+ 'tradetrack-about-label-yourname' => "Label for the user's name field",
 113+ 'tradetrack-about-label-orgname' => "Label for the user's organization's field",
 114+ 'tradetrack-about-expanse-orgname' => 'More information about the organization name field.',
 115+ 'tradetrack-about-label-email' => "Label for the user's email address field",
 116+ 'tradetrack-about-expanse-email' => 'More information about the email address field',
 117+ 'tradetrack-about-label-confirmemail' => "Label for confirming the user's email field",
 118+ 'tradetrack-about-expanse-confirmemail' => 'Describes a bit why we want a confirmation email.',
 119+ 'tradetrack-about-label-phone' => "Label for the user's phone number field",
 120+ 'tradetrack-statement-label' => 'Label for the statement of good faith section.',
 121+ 'tradetrack-statement-expanse' => "This is a legal statement. If translated, there should be a pointer to the English version.",
 122+ 'tradetrack-statement-checkboxlabel' => 'Indicates that the user agrees with the statement',
 123+ 'tradetrack-characters-remaining-notice' => "This is part of a user interface element that counts remaining characters in a specific field. The number of characters is handled through code, and it appears as 'XX Characters Remaining'",
 124+ 'tradetrack-button-continue' => 'This button indicates moving forward to the next step',
 125+ 'tradetrack-button-back' => 'This button indicates heading backwards.',
 126+ 'tradetrack-button-submit' => 'This button indicates submitting.',
 127+ 'tradetrack-nonprofit-preexisting-agreement-question' => 'This asks a question as to whether or not the user has an existing agreement with the WMF.',
 128+ 'tradetrack-nonprofit-preexisting-agreement-yes' => 'Indicates that the user organization has an existing agreement with the WMF',
 129+ 'tradetrack-nonprofit-preexisting-agreement-no-unaffilliated' => 'Indicates that the user does not have an existing agreement with the WMF',
 130+ 'tradetrack-nonprofit-preexisting-agreement-no-mistake' => 'Indicates that the user does not have an agreement, and needs to go back to the previous screen.',
 131+ 'tradetrack-thanks-header' => 'A header for the final success screen',
 132+ 'tradetrack-thanks-text' => 'Text that thanks the user for submitting a request',
 133+ 'tradetrack-errors-have-happened' => 'A generic error message that tells the user that one or more errors have occurred and that they should be addressed below.',
 134+ 'tradetrack-errors-pacman-death' => 'A catch-all error string for a *fatal*, unrecoverable error.',
 135+ 'tradetrack-errors-no-route' => 'Error indicating that the user must select a purpose for their request.',
 136+ 'tradetrack-errors-invalid-route' => 'Error indicating that the purpose provided was invalid',
 137+ 'tradetrack-errors-noncom-no-selection' => 'Error indicating that the user must select whether or not they have a non-commercial agreement.',
 138+ 'tradetrack-errors-noncom-invalid-selection' => 'Error indicating that the non-commercial agreement type is invalid.',
 139+ 'tradetrack-errors-zero-marks' => "Error indicating that the user hasn't selected any trademarks for use.",
 140+ 'tradetrack-errors-other-set-but-not-checked' => 'Error indicating that the user entered a value for "other" but did not check the box.',
 141+ 'tradetrack-errors-missing-other-value' => 'Error indicating that the user selected the "other" value but did not provide any data for it.',
 142+ 'tradetrack-errors-other-too-long' => 'Error indicating that the field in question has gone over maximum length. This message is specifically keyed to the "other" value in the marks list.',
 143+ 'tradetrack-errors-generic-empty' => 'Error indicating that a field is required.',
 144+ 'tradetrack-errors-generic-too-long' => 'Error indicating that the field in question has gone over maximum length.',
 145+ 'tradetrack-errors-emails-do-not-match' => 'Error indicatingt that the email addresses supplied do not match.',
 146+ 'tradetrack-errors-email-fails-regex' => 'Error indicating that the email address supplied is invalid.',
 147+ 'tradetrack-errors-no-accept-statement' => 'Error informing the user that they must agree to the statement of good faith',
 148+);
Index: trunk/extensions/TradeTrack/css/TradeTrack.css
@@ -0,0 +1,92 @@
 2+#tradetrack-screens {
 3+ margin: 1em auto 1em auto;
 4+ padding: 10px;
 5+ width:500px;
 6+ background: #EEEEEE;
 7+}
 8+#tradetrack-screens input[type='text'], #tradetrack-screens textarea {
 9+ width: 70%;
 10+ margin-left: 10px;
 11+}
 12+#tradetrack-screens textarea {
 13+ margin-bottom: 0px;
 14+}
 15+.characters-remaining-box { display: none; }
 16+html > body #tradetrack-screens .characters-remaining-box {
 17+ display:block;
 18+ margin-top: -5px;
 19+ width: 70%;
 20+ margin-left: 10px;
 21+ text-align: right;
 22+ font-size: 0.8em;
 23+ color: #4d4d4d;
 24+}
 25+#tradetrack-screens .characters-remaining-box .tradetrack-toomany {
 26+ color: #a31205;
 27+}
 28+div.tradetrack-element, div.tradetrack-element-error {
 29+ padding: 5px;
 30+ margin-bottom: 5px;
 31+}
 32+
 33+div.tradetrack-element {
 34+ border: 1px solid #EEEEEE;
 35+}
 36+div.tradetrack-element-error {
 37+ border: 1px solid #a91100;
 38+}
 39+div.tradetrack-button-box {
 40+ text-align: right;
 41+}
 42+.tradetrack-button {
 43+}
 44+.tradetrack-question-expanse {
 45+ font-size: 0.8em;
 46+}
 47+#tradetrack-screens ul.tradetrack-element-list {
 48+ margin-left: 20px;
 49+ list-style: none;
 50+ list-style-image: none;
 51+ list-style-type: none;
 52+}
 53+.tradetrack-element-list li {
 54+ list-style-type: none;
 55+}
 56+.tradetrack-errornotice {
 57+ margin: 1em auto 1em auto;
 58+ padding: 5px;
 59+ background: #e8e8e8;
 60+ border: 2px solid #a91100;
 61+}
 62+ul.tradetrack-errors {
 63+ margin-left: 10px;
 64+ list-style-type: none;
 65+ color: #a91100;
 66+ list-style-image: none;
 67+}
 68+ul.tradetrack-errors li {
 69+ list-style-type: none;
 70+}
 71+label.tradetrack-question-label {
 72+ font-weight: bold;
 73+ float: left;
 74+}
 75+.tradetrack-field-hint {
 76+ width: 11px;
 77+ height: 24px;
 78+ display: block;
 79+ float: left;
 80+ margin-left: 5px;
 81+ margin-top: -3px;
 82+ background: url(../images/question.gif) 0 50% no-repeat;
 83+}
 84+.tradetrack-field-hint:hover {
 85+ background: url(../images/question-hover.gif) 0 50% no-repeat;
 86+}
 87+/* Tipsy Styles */
 88+.tipsy { padding: 5px 5px 10px; font-size: 12px; position: absolute; z-index: 100000; overflow: visible; }
 89+.tipsy-inner { padding: 5px 8px 4px 8px; background-color: #d6f3ff; color: black; border: 1px solid #5dc9f4; max-width: 300px; text-align: left; }
 90+.tipsy-arrow { position: absolute; background: url( '../images/arrow.gif' ) no-repeat top left; width: 13px; height: 13px; }
 91+.tipsy-se .tipsy-arrow { bottom: -2px; right: 10px; background-position: 0% 100%; }
 92+
 93+/* End Tipsy styles */
Index: trunk/extensions/TradeTrack/TradeTrack.php
@@ -0,0 +1,30 @@
 2+<?php
 3+// Process system for managing trademark usage requests.
 4+
 5+$wgExtensionCredits['specialpage'][] = array(
 6+ 'path' => __FILE__,
 7+ 'name' => 'Trade Track',
 8+ 'author' => array( 'Brandon Harris' ),
 9+ 'url' => 'http://www.mediawiki.org/wiki/Extension:TradeTrack',
 10+ 'descriptionmsg' => 'tradetrack-desc',
 11+);
 12+
 13+$wgSpecialPages['TradeTrack'] = 'SpecialTradeTrack';
 14+
 15+$wgTradeTrackEmailSubject = "A new Trademark Request has Arrived";
 16+$wgTradeTrackFromEmail = "tradetrack@wikimedia.org";
 17+
 18+$wgAutoloadClasses['SpecialTradeTrack'] = dirname(__FILE__) . "/SpecialTradeTrack.php";
 19+$wgAutoloadClasses['TradeTrackScreen'] = dirname(__FILE__) . "/templates/TradeTrackScreen.php";
 20+$wgAutoloadClasses['TradeTrackScreenDetailsForm'] = dirname(__FILE__) . "/templates/TradeTrackScreenDetailsForm.php";
 21+$wgAutoloadClasses['TradeTrackScreenNonComAgreement'] = dirname(__FILE__) . "/templates/TradeTrackScreenNonComAgreement.php";
 22+$wgAutoloadClasses['TradeTrackScreenRouting'] = dirname(__FILE__) . "/templates/TradeTrackScreenRouting.php";
 23+$wgAutoloadClasses['TradeTrackScreenThanks'] = dirname(__FILE__) . "/templates/TradeTrackScreenThanks.php";
 24+$wgAutoloadClasses['TradeTrackEmail'] = dirname(__FILE__) . "/templates/TradeTrackEmail.php";
 25+
 26+$wgExtensionMessagesFiles['TradeTrack'] = dirname( __FILE__ ) . "/TradeTrack.i18n.php";
 27+
 28+$wgTradeTrackEmailCommercial = "bharris@wikimedia.org"; // Who gets commercial requests (Kul)
 29+$wgTradeTrackEmailNonCommercial = "bharris@wikimedia.org"; // Who gets non-commercial requests (Mike)
 30+$wgTradeTrackEmailMedia = "bharris@wikimedia.org"; // Who gets media requests (Jay)
 31+
Index: trunk/extensions/TradeTrack/SpecialTradeTrack.php
@@ -0,0 +1,591 @@
 2+<?php
 3+
 4+class SpecialTradeTrack extends SpecialPage {
 5+
 6+
 7+ /**
 8+ * This is an array of the various trademarks that we're watching over.
 9+ */
 10+ private static $TRADEMARK_LIST = array(
 11+ 'wmf',
 12+ 'wikipedia',
 13+ 'wiktionary',
 14+ 'wikiquote',
 15+ 'wikibooks',
 16+ 'wikiversity',
 17+ 'wikispecies',
 18+ 'wikisource',
 19+ 'mediawiki',
 20+ 'wikimediacommons',
 21+ 'wikimediaincubator',
 22+ 'wikinews',
 23+ 'other',
 24+ );
 25+
 26+ /**
 27+ * This defines the prefix for (most) all of our form field elements
 28+ */
 29+ private static $VARIABLE_PREFIX = "tradetrack-elements-";
 30+
 31+ /**
 32+ * This is our validation framework array. It is requied by validateField(),
 33+ * below.
 34+ *
 35+ * For each field that we are going to validate, we need an entry in the
 36+ * array. That entry will contain two additional arrays: 'errmsgs' and
 37+ * 'tests'.
 38+ *
 39+ * The tests array defines each test type for the field and
 40+ * possible thresholds or arguments to the test (e.g., max length).
 41+ *
 42+ * The errmsgs array has named entries for the each test and defines which
 43+ * message string should be shoved into the errors field if the test fails.
 44+ *
 45+ * This could probably better be handled with a single array set, thus:
 46+ *
 47+ * $fieldname => array(
 48+ * 'testname' => array(
 49+ * 'threshold' => $value,
 50+ * 'error' => $value,
 51+ * ),
 52+ * );
 53+ * I will probably refactor to that but want to get it working first.
 54+ *
 55+ * I would have loved to use a constant here but that threw some
 56+ * errors so I have to use the real number.
 57+ */
 58+ private static $VALIDATION_FIELDS = array(
 59+ 'usage' => array(
 60+ 'errmsgs' => array(
 61+ 'max' => 'tradetrack-errors-generic-too-long',
 62+ 'required' => 'tradetrack-errors-generic-empty',
 63+ ),
 64+ 'tests' => array(
 65+ 'max' => 5000,
 66+ 'required' => true
 67+ ),
 68+ ),
 69+ 'mailingaddress' => array(
 70+ 'errmsgs' => array(
 71+ 'max' => 'tradetrack-errors-generic-too-long',
 72+ 'required' => 'tradetrack-errors-generic-empty',
 73+ ),
 74+ 'tests' => array(
 75+ 'max' => 5000,
 76+ 'required' => true
 77+ ),
 78+ ),
 79+ 'name' => array(
 80+ 'errmsgs' => array(
 81+ 'max' => 'tradetrack-errors-generic-too-long',
 82+ 'required' => 'tradetrack-errors-generic-empty',
 83+ ),
 84+ 'tests' => array(
 85+ 'max' => 200,
 86+ 'required' => true
 87+ ),
 88+ ),
 89+ 'orgname' => array(
 90+ 'errmsgs' => array(
 91+ 'max' => 'tradetrack-errors-generic-too-long',
 92+ 'required' => 'tradetrack-errors-generic-empty',
 93+ ),
 94+ 'tests' => array(
 95+ 'max' => 200,
 96+ 'required' => true
 97+ ),
 98+ ),
 99+ 'email' => array(
 100+ 'errmsgs' => array(
 101+ 'max' => 'tradetrack-errors-generic-too-long',
 102+ 'required' => 'tradetrack-errors-generic-empty',
 103+ 'equals' => 'tradetrack-errors-emails-do-not-match'
 104+ ),
 105+ 'tests' => array(
 106+ 'max' => 200,
 107+ 'required' => true,
 108+ 'equals' => 'confirmemail'
 109+ ),
 110+ ),
 111+ 'confirmemail' => array(
 112+ 'errmsgs' => array(
 113+ 'max' => 'tradetrack-errors-generic-too-long',
 114+ 'required' => 'tradetrack-errors-generic-empty',
 115+ ),
 116+ 'tests' => array(
 117+ 'max' => 200,
 118+ 'required' => true
 119+ ),
 120+ ),
 121+ 'phone' => array(
 122+ 'errmsgs' => array(
 123+ 'max' => 'tradetrack-errors-generic-too-long',
 124+ 'required' => 'tradetrack-errors-generic-empty',
 125+ ),
 126+ 'tests' => array(
 127+ 'max' => 200,
 128+ 'required' => true
 129+ ),
 130+ ),
 131+ 'statementagreement' => array(
 132+ 'errmsgs' => array(
 133+ 'required' => 'tradetrack-errors-no-accept-statement',
 134+ ),
 135+ 'tests' => array(
 136+ 'required' => true
 137+ ),
 138+ ),
 139+ 'otherval' => array(
 140+ 'errmsgs' => array(
 141+ 'max' => 'tradetrack-errors-generic-too-long',
 142+ 'requiredif' => 'tradetrack-errors-missing-other-value',
 143+ 'emptyunless' => 'tradetrack-errors-other-set-but-not-checked',
 144+ ),
 145+ 'tests' => array(
 146+ 'max' => 200,
 147+ 'requiredif' => 'tradetrack-which-other',
 148+ 'emptyunless' => 'tradetrack-which-other',
 149+ ),
 150+ ),
 151+ );
 152+
 153+ /**
 154+ * Some private arrays to point to our resources.
 155+ */
 156+ private static $styleFiles = array(
 157+ array( 'src' => 'css/TradeTrack', 'version' => 1 ),
 158+ );
 159+
 160+ private static $scriptFiles = array(
 161+ array( 'src' => 'js/TradeTrack.js', 'version' => 1 ),
 162+ array( 'src' => 'js/jquery.tipsy.js', 'version' => 1 ),
 163+ array( 'src' => 'js/jquery.NobleCount.js', 'version' => 1 ),
 164+ );
 165+
 166+ private static $messages = array();
 167+ private static $scripts = array();
 168+
 169+ /**
 170+ * This is our errors array.
 171+ */
 172+ private $errors = array();
 173+
 174+ function __construct() {
 175+ parent::__construct( 'TradeTrack' );
 176+ wfLoadExtensionMessages( 'TradeTrack' );
 177+ }
 178+
 179+ /**
 180+ * Adds an error to the local error stack for later display. The local errors array is
 181+ * actually a matrix where each "key" points to another array.
 182+ *
 183+ * Note that we need to be adding *parsed* error messages here. Why? Because there isn't an easy
 184+ * way to send $1, $2, etc. down the pipe and into the templates.
 185+ *
 186+ * @param target The named target for the error (a single target can have multiple errors)
 187+ * @param errorString The message to throw into the error. This should be an i18n pointer.
 188+ */
 189+ function addError($target, $errorString) {
 190+ $eList = $errorsArray[$target];
 191+ if (!$eList) { $eList = array( ); }
 192+ array_push($eList, $errorString);
 193+
 194+ $this->errors[$target] = $eList;
 195+ }
 196+
 197+ /**
 198+ * Simply returns true or false if there have been errors thrown during the run
 199+ *
 200+ * @return true if there are errors; false otherwise.
 201+ */
 202+ function hasErrors() {
 203+ if ( count( $this->errors ) != 0 ) { return true; }
 204+ return false;
 205+ }
 206+
 207+
 208+ function execute( $par ) {
 209+ global $wgRequest, $wgOut;
 210+
 211+ global $wgExtensionAssetsPath;
 212+
 213+ foreach ( self::$scriptFiles as $script ) {
 214+ $wgOut->addScriptFile( $wgExtensionAssetsPath . "/TradeTrack/{$script['src']}", $script['version'] );
 215+ }
 216+
 217+ foreach ( self::$styleFiles as $style ) {
 218+ $wgOut->addExtensionStyle( $wgExtensionAssetsPath . "/TradeTrack/{$style['src']}?{$style['version']}" );
 219+ }
 220+
 221+
 222+ $wgOut->setPageTitle( wfMsg( 'tradetrack-header' ) );
 223+
 224+ // This is our template data array.
 225+ $tData = array();
 226+ $tData['formURL'] = $this->getTitle()->getLinkURL( $query );
 227+
 228+ // First, see if it's supplied from the page.
 229+ $doaction = $wgRequest->getVal( 'doaction' );
 230+
 231+
 232+ $success = false;
 233+
 234+ // open wide.
 235+ ob_start();
 236+
 237+
 238+ switch( $doaction ) {
 239+ case 'route':
 240+ /*
 241+ * The user has selected (or failed to select) the first step on their way to Mordor.
 242+ * Are we going overland, or through the Mines of Moria?
 243+ */
 244+ $purpose = $wgRequest->getVal( 'tradetrack-purpose' );
 245+ if ( !isset( $purpose ) ) {
 246+ $this->addError( 'tradetrack-purpose', wfMsg( 'tradetrack-errors-no-route' ) );
 247+ } else if ( ( $purpose != 'Commercial' )
 248+ && ( $purpose != 'Non-Commercial' )
 249+ && ( $purpose != 'Media' ) ) {
 250+ $this->addError( 'tradetrack-purpose', wfMsg( 'tradetrack-errors-invalid-route' ) );
 251+ }
 252+
 253+ if ( $this->hasErrors() ) {
 254+ $tmp = new TradeTrackScreenRouting();
 255+ } else {
 256+ $tData['purpose'] = $purpose;
 257+ if ( $purpose == 'Non-Commercial' ) {
 258+ $tmp = new TradeTrackScreenNonComAgreement();
 259+ } else {
 260+ $tmp = new TradeTrackScreenDetailsForm();
 261+ }
 262+ }
 263+ break;
 264+ case 'noncomroute':
 265+ /*
 266+ * Over land it is.
 267+ * This is an interleave screen, only available if you select "Non-Commercial."
 268+ * This is mostly for data collection.
 269+ * The user will still be forced to deal with the Orks in Moria.
 270+ */
 271+ $purpose = $wgRequest->getVal( 'tradetrack-purpose' );
 272+ if ( !isset( $purpose ) ) {
 273+ // Ensure we still know what we're doing. If not, bail with the wah-wah sound.
 274+ $this->addError( 'global', wfMsg( 'tradetrack-errors-pacman-death' ) );
 275+ $tmp = new TradeTrackScreenRouting();
 276+ break;
 277+ }
 278+
 279+ $tData['purpose'] = $purpose;
 280+
 281+ $agreementType = $wgRequest-> getVal( 'tradetrack-elements-agreement' );
 282+ if ( !isset( $agreementType ) ) {
 283+ $this->addError( 'tradetrack-elements-agreement', wfMsg( 'tradetrack-errors-noncom-no-selection' ) );
 284+ } else if ( ( $agreementType != 'Yes' )
 285+ && ( $agreementType != 'No' )
 286+ && ( $agreementType != 'Mistake' ) ) {
 287+ $this->addError( 'tradetrack-elements-agreement', wfMsg( 'tradetrack-errors-noncom-invalid-selection' ) );
 288+ }
 289+ if ( $this->hasErrors() ) {
 290+ // Oh no! The thing in the lake grabbed Frodo!
 291+ $tmp = new TradeTrackScreenNonComAgreement();
 292+ } else if ( $agreementType == 'Mistake' ) {
 293+ // For completeness' sake, we must give the user an escape route.
 294+ // Frodo decides to ditch the entire process and marry some poor hobbit back
 295+ // in the Shire.
 296+ $tmp = new TradeTrackScreenRouting();
 297+ } else {
 298+ // Why yes, I do know the Elvish word for "Friend".
 299+ // No errors of any kind, we're going in.
 300+ $tData['agreementType'] = $agreementType;
 301+ $tmp = new TradeTrackScreenDetailsForm();
 302+ }
 303+ break;
 304+ case 'details':
 305+ /*
 306+ * This is the final screen. This has several fields, all of which are required.
 307+ *
 308+ * You are in a maze of twisty passages, all alike. You may be eaten by a Balrog.
 309+ */
 310+
 311+ $purpose = $wgRequest->getVal( 'tradetrack-purpose' );
 312+ if ( !isset( $purpose ) ) {
 313+ // Ensure we still know what we're doing. If not, bail with the wah-wah sound.
 314+ $this->addError( 'global', wfMsg( 'tradetrack-errors-pacman-death' ) );
 315+ $tmp = new TradeTrackScreenRouting();
 316+ break;
 317+ }
 318+ $tData['purpose'] = $purpose;
 319+
 320+ // Handle lost agreement type, if required.
 321+ $agreementType = $wgRequest-> getVal( 'tradetrack-elements-agreement' );
 322+ if ( ( !$agreementType) && ( $purpose == 'Non-Commercial' ) ) {
 323+ $this->addError( 'global', wfMsg( 'tradetrack-errors-pacman-death' ) );
 324+ $tmp = new TradeTrackScreenRouting();
 325+ break;
 326+ }
 327+ $tData['agreementType'] = $agreementType;
 328+
 329+ // Let's get the easy fields out of the way first.
 330+
 331+
 332+ $checkElements = array(
 333+ 'usage',
 334+ 'mailingaddress',
 335+ 'name',
 336+ 'orgname',
 337+ 'email',
 338+ 'confirmemail',
 339+ 'phone',
 340+ 'statementagreement',
 341+ 'otherval'
 342+ );
 343+
 344+
 345+ // This runs validation on the bulk of our fields.
 346+ foreach ( $checkElements as $e ) {
 347+ $this->validateField( $e, $wgRequest );
 348+ $tData[$e] = $wgRequest->getVal(self::$VARIABLE_PREFIX . $e); // Shove into template data array
 349+ }
 350+
 351+
 352+ // Now we cycle through the trademarks list and see if any of them are set.
 353+ $tData['trademarks'] = array();
 354+
 355+ foreach ( self::$TRADEMARK_LIST as $property ) {
 356+ if ( $wgRequest->getBool( "tradetrack-which-$property" ) ) {
 357+ $tData['trademarks'][$property] = $property;
 358+ }
 359+ }
 360+
 361+ if ( count( $tData['trademarks'] ) == 0 ) {
 362+ // Didn't select a single mark
 363+ $this->addError( 'tradetrack-element-list', wfMsg( 'tradetrack-errors-zero-marks' ) );
 364+ }
 365+
 366+
 367+ // Now, if errors, kick back.
 368+ if ( $this->hasErrors() ) {
 369+ // Still stuck in the mines.
 370+ $tmp = new TradeTrackScreenDetailsForm();
 371+ } else {
 372+ // Yay! Now we get to frolic with the Elves.
 373+ $success = true;
 374+ $tmp = new TradeTrackScreenThanks();
 375+ }
 376+
 377+ break;
 378+ default:
 379+ /*
 380+ * This is the first time to the page, or there has been some sort of
 381+ * unrecoverable error. Ladies and gentlemen, I give you the first screen.
 382+ */
 383+ $tmp = new TradeTrackScreenRouting();
 384+ break;
 385+ }
 386+
 387+ if ( ( isset( $tmp ) ) && ( $tmp instanceof QuickTemplate ) ) {
 388+
 389+ // Stick the trademark list into the template's data space.
 390+ $tData['TRADEMARK_LIST'] = self::$TRADEMARK_LIST;
 391+
 392+ // Add in spices.
 393+ $tmp->set( 'tData', $tData );
 394+ $tmp->set( 'errors', $this->errors );
 395+
 396+ // Bake at 300 degrees for 20 minutes.
 397+ $tmp->execute();
 398+ }
 399+
 400+ $wgOut->addHtml( ob_get_clean() );
 401+
 402+ if ( $success ) {
 403+
 404+ // Change the page title
 405+ $wgOut->setPageTitle( wfMsg( 'tradetrack-thanks-header' ) );
 406+
 407+
 408+ // Insert to the database.
 409+ $this->insertTradeTrackRequest( $tData );
 410+
 411+ // Now build the email
 412+ global $wgTradeTrackEmailCommercial;
 413+ global $wgTradeTrackEmailNonCommercial;
 414+ global $wgTradeTrackEmailMedia;
 415+ global $wgTradeTrackEmailSubject;
 416+ global $wgTradeTrackFromEmail;
 417+
 418+ $toEmail = "";
 419+
 420+ // Who gets the email?
 421+ switch ( $tData['purpose'] ) {
 422+ case 'Commercial':
 423+ $toEmail = $wgTradeTrackEmailCommercial;
 424+ break;
 425+ case 'Non-Commercial':
 426+ $toEmail = $wgTradeTrackEmailNonCommercial;
 427+ break;
 428+ case 'Media':
 429+ $toEmail = $wgTradeTrackEmailMedia;
 430+ break;
 431+ default:
 432+ $toEmail = $wgTradeTrackEmailNonCommercial;
 433+ break;
 434+ }
 435+
 436+ ob_start();
 437+
 438+ $emailTmp = new TradeTrackEmail();
 439+ $emailTmp->set( 'tData', $tData );
 440+ $emailTmp->execute();
 441+ $generatedEmail = ob_get_clean();
 442+ $mailer = new UserMailer();
 443+
 444+ $mailer->send( new MailAddress( $toEmail ) , new MailAddress( $wgTradeTrackFromEmail ), $wgTradeTrackEmailSubject, $generatedEmail);
 445+
 446+ // debug line to dump this to the end screen.
 447+ $wgOut->addHtml( $generatedEmail );
 448+
 449+
 450+ }
 451+
 452+ }
 453+
 454+ /**
 455+ * This does field validation. It looks up fields in the VALIDATION_FIELDS array
 456+ * and runs tests on them. This, combined with VALIDATION_FIELDS, is really a
 457+ * crude validation framework.
 458+ *
 459+ * Entries in the VALIDATION_FIELDS array must be named the same as the field in the
 460+ * html form, *minus* the value of $VARIABLE_PREFIX.
 461+ *
 462+ * This method takes the $wgRequest object as a variable rather than the possible
 463+ * value supplied by the form. The reason for this is that one of the tests is
 464+ * "equals", which makes sure two form values are the same (e.g., email and
 465+ * emailconfirm).
 466+ *
 467+ * This method *also* takes the supplied values and sticks them back into the
 468+ * page's global "data" array, so that they can be redisplayed if there are errors.
 469+ *
 470+ * This could (should) probably be pulled out into a separate class.
 471+ *
 472+ * @param fieldName the name of the form field to validate
 473+ * @param $request the $wgRequest object.
 474+ */
 475+ function validateField( $fieldName, $request ) {
 476+
 477+ $value = $request->getVal( self::$VARIABLE_PREFIX . $fieldName );
 478+
 479+ // No need to validate. This field has no tests.
 480+ if ( !isset ( self::$VALIDATION_FIELDS[$fieldName] ) ) {
 481+ return true;
 482+ }
 483+
 484+ foreach ( self::$VALIDATION_FIELDS[$fieldName]['tests'] as $vType => $vThreshold ) {
 485+
 486+ switch ( $vType ) {
 487+ case 'max':
 488+ // Tests that the value does not exceed a threshold, defined in the array (max => threshold)
 489+ if ( ( isset( $value ) ) && ( strlen( $value ) > $vThreshold ) ) {
 490+ $this->addError( "tradetrack-elements-$fieldName", wfMsg( self::$VALIDATION_FIELDS[$fieldName]['errmsgs'][$vType], array( $vThreshold ) ) );
 491+ }
 492+ break;
 493+ case 'required':
 494+ // Tests to ensure that the value exists.
 495+ if ( !$value ) {
 496+ $this->addError( "tradetrack-elements-$fieldName", wfMsg( self::$VALIDATION_FIELDS[$fieldName]['errmsgs'][$vType] ) );
 497+ }
 498+ break;
 499+ case 'requiredif':
 500+ // This field is only required if another field is set as well.
 501+ // Note that we do NOT use variable prefix here.
 502+ $required = $request->getVal( $vThreshold );
 503+ if ( ( !$value ) && ( isset( $required ) ) ) {
 504+ $this->addError( "tradetrack-elements-$fieldName", wfMsg( self::$VALIDATION_FIELDS[$fieldName]['errmsgs'][$vType] ) );
 505+ }
 506+ break;
 507+ case 'emptyunless':
 508+ // This field should be empty unless another field is set.
 509+ $unless = $request->getVal( $vThreshold );
 510+ if ( ( $value ) && ( !isset( $unless ) ) ) {
 511+ $this->addError( "tradetrack-elements-$fieldName", wfMsg( self::$VALIDATION_FIELDS[$fieldName]['errmsgs'][$vType] ) );
 512+ }
 513+ break;
 514+ case 'equals':
 515+ // Tests that the field value is equal to the value of another feild ('equals' => comparison field)
 516+ if ( isset( $value ) ) {
 517+ $compare = $request->getVal( self::$VARIABLE_PREFIX . $vThreshold );
 518+ if ( ( !isset( $compare ) ) || ( $value !== $compare ) ) {
 519+ $this->addError( "tradetrack-elements-$fieldName", wfMsg( self::$VALIDATION_FIELDS[$fieldName]['errmsgs'][$vType] ) );
 520+ }
 521+ }
 522+ break;
 523+ default:
 524+ break;
 525+ }
 526+ }
 527+ return true;
 528+ }
 529+
 530+
 531+ function sendEmailMail( array $tData ) {
 532+
 533+
 534+ }
 535+
 536+
 537+ private function insertTradeTrackRequest( array $tData ) {
 538+ $dbw = wfGetDB( DB_MASTER );
 539+
 540+ // Ugly.
 541+ $markMapping = array();
 542+
 543+ $res = $dbw->select(
 544+ 'tradetrack_trademarks',
 545+ array ('tt_mark_id', 'tt_mark')
 546+ );
 547+ foreach ( $res as $row ) {
 548+ $markMapping[$row->tt_mark] = $row->tt_mark_id;
 549+ }
 550+
 551+ $timestamp = $dbw->timestamp();
 552+
 553+ $dbw->insert(
 554+ 'tradetrack_requests',
 555+ array(
 556+ 'tt_purpose' => $tData['purpose'],
 557+ 'tt_agreement' => $tData['agreementType'],
 558+ 'tt_name' => $tData['name'],
 559+ 'tt_email' => $tData['email'],
 560+ 'tt_orgname' => $tData['orgname'],
 561+ 'tt_otherval' => $tData['otherval'],
 562+ 'tt_phone' => $tData['phone'],
 563+ 'tt_usage' => $tData['usage'],
 564+ 'tt_mailingaddress' => $tData['mailingaddress'],
 565+ 'tt_timestamp' => $timestamp,
 566+ ),
 567+ __METHOD__,
 568+ array( 'IGNORE' )
 569+ );
 570+
 571+ $lastId = $dbw->insertId();
 572+ // Now we insert rows for every mark requested.
 573+
 574+ foreach ( $tData['trademarks'] as $trademark ) {
 575+ $dbw->insert(
 576+ 'tradetrack_mark_requests',
 577+ array(
 578+ 'tt_request_id' => $lastId,
 579+ 'tt_mark_id' => $markMapping[$trademark],
 580+ 'tt_timestamp' => $timestamp,
 581+ ),
 582+ __METHOD__,
 583+ array( 'IGNORE' )
 584+ );
 585+
 586+ }
 587+
 588+
 589+ }
 590+
 591+
 592+}
Index: trunk/extensions/TradeTrack/js/TradeTrack.js
@@ -0,0 +1,31 @@
 2+( function( $ ) {
 3+ $.TradeTrack = {
 4+
 5+ 'fn' : {
 6+ 'init': function( $$options ) {
 7+ $j( '.tradetrack-field-hint' ).tipsy( { gravity : 'se', opacity: '0.9' } );
 8+ $j( '#tradetrack-elements-usage-textarea' ).NobleCount('#tradetrack-elements-usage-count', {
 9+ max_chars: 5000,
 10+ on_positive: function(t_obj, char_area, c_settings, char_rem){
 11+ $(char_area).removeClass( 'tradetrack-toomany' );
 12+ },
 13+ on_negative: function(t_obj, char_area, c_settings, char_rem){
 14+ $(char_area).addClass( 'tradetrack-toomany' );
 15+ }
 16+ });
 17+ $j( '#tradetrack-elements-mailingaddress-textarea' ).NobleCount('#tradetrack-elements-mailingaddress-count', {
 18+ max_chars: 5000,
 19+ on_positive: function(t_obj, char_area, c_settings, char_rem){
 20+ $(char_area).removeClass( 'tradetrack-toomany' );
 21+ },
 22+ on_negative: function(t_obj, char_area, c_settings, char_rem){
 23+ $(char_area).addClass( 'tradetrack-toomany' );
 24+ }
 25+ });
 26+ },
 27+ }
 28+ };
 29+ $( document ).ready( function () {
 30+ $.TradeTrack.fn.init( );
 31+ } ); //document ready
 32+} )( jQuery );
Index: trunk/extensions/TradeTrack/js/jquery.NobleCount.js
@@ -0,0 +1,480 @@
 2+/******************************************************************************************************
 3+
 4+ jQuery.NobleCount
 5+
 6+ Author Jeremy Horn
 7+ Version 1.0
 8+ Date: 3/21/2010
 9+
 10+ Copyright (c) 2010 Jeremy Horn- jeremydhorn(at)gmail(dot)c0m | http://tpgblog.com
 11+ Dual licensed under MIT and GPL.
 12+
 13+ DESCRIPTION
 14+ NobleCount... for a more 'proper' count of the characters remaining.
 15+
 16+ NobleCount is a customizable jQuery plugin for a more the improved counting of the remaining
 17+ characters, and resulting behaviors, of a text entry object, e.g. input textfield, textarea.
 18+
 19+ As text is entered into the target text area an object for the purposes of tracking
 20+ the total number of characters remaining, defined as the maximum number of characters
 21+ minus the current total number of characters within the text entry object, and storing
 22+ that information visually and/or within the DOM as an HTML 5 compliant data-* attribute.
 23+
 24+ Events and CSS Class alterations, if defined, are triggered based on current user
 25+ interaction with the target text entry object as well as the current state (positive or
 26+ negative) of the character remaining value.
 27+
 28+ NobleCount supports pre-existing text within the text object.
 29+ NobleCount supports jQuery chaining.
 30+
 31+ Within NobleCount context...
 32+ NEGATIVE is defined as Integers < 0
 33+ POSITIVE is defined as Integers >= 0 [on_positive will fire when char_rem == 0]
 34+
 35+ BY DEFAULT
 36+ - maximum characters EQUAL 140 characters
 37+ - no events defined
 38+ - no class changes defined
 39+ - no DOM attributes are created/altered
 40+ - user permitted to type past the maximum number of characters limit, resulting in
 41+ negative number of characters remaining
 42+
 43+ IMPLEMENTATION
 44+
 45+ $('#textarea1').NobleCount('#characters_remaining1');
 46+ $('#textfield2').NobleCount('#characters_remaining2', { / * OPTIONS * / });
 47+
 48+ COMPATIBILITY
 49+
 50+ Tested in FF3.5, IE7
 51+ With jQuery 1.3.x, 1.4.x
 52+
 53+ METHOD(S)
 54+ To properly intialize, both the text entry object and the object that will store the
 55+ total number of characters remaining must exist and be passed to NobleCount.
 56+
 57+ $(TEXT_ENTRY_OBJECT).NobleCount(CHARACTERS_REMAINING_OBJECT);
 58+
 59+ Any callback functions assigned to any of the availale events are passed the following
 60+ parameters: t_obj, char_area, c_settings, char_rem
 61+
 62+ t_obj text entry object
 63+
 64+ char_area selection of the characters remaining object
 65+
 66+ c_settings result of the options passed into NobleCount at time of
 67+ initialization merged with the default options
 68+
 69+ ** this is a GREAT way to pass in and remember other state
 70+ information that will be needed upon the triggering of
 71+ NobleCount events **
 72+
 73+ char_rem integer representation of the total number of characters
 74+ remaining resulting from the calculated difference between
 75+ the target maximum number of characters and the current
 76+ number of characters currently within t_obj
 77+
 78+ Both TEXT_ENTRY_OBJECT and CHARACTERS_REMAINING_OBJECT must be specified and valid.
 79+
 80+ Upon successful initialization, all appropriate events and classes are applied to
 81+ the CHARACTERS_REMAINING_OBJECT, including the storage (if not disabled) visually
 82+ or only in the DOM (if enabled) of the integer representing the number of characters
 83+ remaining.
 84+
 85+ The target maximum number of characters (max_chars) are determined by the following
 86+ precedence rules....
 87+
 88+ if max_chars passed via constructor
 89+ max_chars = max_chars passed
 90+ else if number exists within characters_remaining object and number > 0
 91+ max_chars = number within the text() of characters_remaining object
 92+ else use the NobleCount's default max_chars
 93+
 94+ CUSTOMIZATION
 95+
 96+ NobleCount(c_obj, <OPTIONS>)
 97+ e.g. $(t_obj).NobleCount(c_obj, {max_chars:100px});
 98+
 99+
 100+ on_negative class (STRING) or FUNCTION that is applied/called
 101+ when characters remaining is negative IF DEFINED
 102+
 103+ on_positive class (STRING) or FUNCTION that is applied/called
 104+ when characters remaining is positive IF DEFINED
 105+
 106+ on_update FUNCTION that is called when characters remaining changes
 107+
 108+ max_chars target maximum number of characters
 109+
 110+ block_negative if TRUE, then all attempts are made to block entering
 111+ more than max_characters; not effective against user
 112+ pasting in blocks of text that exceed the max_chars value
 113+ otherwise, text area will let individual entering the text
 114+ to exceed max_chars limit (characters remaining becomes
 115+ negative)
 116+
 117+ cloak: false, if TRUE, then no visual updates of characters remaining
 118+ object (c_obj) will occur; this does not have any effect
 119+ on the char_rem value returned via any event callbacks
 120+ otherwise, the text within c_obj is constantly updated to
 121+ represent the total number of characters remaining until
 122+ the max_chars limit has been reached
 123+
 124+ in_dom: false if TRUE and cloak is ALSO TRUE, then the number of characters
 125+ remaining are stored as the attribute of c_obj
 126+ named 'data-noblecount'
 127+
 128+ !NOTE: if enabled, due to constant updating of a DOM element
 129+ attribute user experience can appear sluggish while
 130+ the individual is modifying the text entry object (t_obj)
 131+
 132+
 133+ EXAMPLE OPTIONS =
 134+ {
 135+ on_negative: 'go_red',
 136+ on_positive: 'go_green',
 137+ max_chars: 25,
 138+ on_update: function(t_obj, char_area, c_settings, char_rem){
 139+ if ((char_rem % 10) == 0) {
 140+ char_area.css('font-weight', 'bold');
 141+ char_area.css('font-size', '300%');
 142+ } else {
 143+ char_area.css('font-weight', 'normal');
 144+ char_area.css('font-size', '100%');
 145+ }
 146+ }
 147+ };
 148+
 149+ MORE
 150+
 151+ For more details about NobleCount, its implementation, usage, and examples, go to:
 152+ http://tpgblog.com/noblecount/
 153+
 154+******************************************************************************************************/
 155+
 156+(function($) {
 157+
 158+ /**********************************************************************************
 159+
 160+ FUNCTION
 161+ NobleCount
 162+
 163+ DESCRIPTION
 164+ NobleCount method constructor
 165+
 166+ allows for customization of maximum length and related update/length
 167+ behaviors
 168+
 169+ e.g. $(text_obj).NobleCount(characters_remaining_obj);
 170+
 171+ REQUIRED: c_obj
 172+ OPTIONAL: options
 173+
 174+ **********************************************************************************/
 175+
 176+ $.fn.NobleCount = function(c_obj, options) {
 177+ var c_settings;
 178+ var mc_passed = false;
 179+
 180+ // if c_obj is not specified, then nothing to do here
 181+ if (typeof c_obj == 'string') {
 182+ // check for new & valid options
 183+ c_settings = $.extend({}, $.fn.NobleCount.settings, options);
 184+
 185+ // was max_chars passed via options parameter?
 186+ if (typeof options != 'undefined') {
 187+ mc_passed = ((typeof options.max_chars == 'number') ? true : false);
 188+ }
 189+
 190+ // process all provided objects
 191+ return this.each(function(){
 192+ var $this = $(this);
 193+
 194+ // attach events to c_obj
 195+ attach_nobility($this, c_obj, c_settings, mc_passed);
 196+ });
 197+ }
 198+
 199+ return this;
 200+ };
 201+
 202+
 203+ /**********************************************************************************
 204+
 205+ FUNCTION
 206+ NobleCount.settings
 207+
 208+ DESCRIPTION
 209+ publically accessible data stucture containing the max_chars and
 210+ event handling specifications for NobleCount
 211+
 212+ can be directly accessed by '$.fn.NobleCount.settings = ... ;'
 213+
 214+ **********************************************************************************/
 215+ $.fn.NobleCount.settings = {
 216+
 217+ on_negative: null, // class (STRING) or FUNCTION that is applied/called
 218+ // when characters remaining is negative
 219+ on_positive: null, // class (STRING) or FUNCTION that is applied/called
 220+ // when characters remaining is positive
 221+ on_update: null, // FUNCTION that is called when characters remaining
 222+ // changes
 223+ max_chars: 140, // maximum number of characters
 224+ block_negative: false, // if true, then all attempts are made to block entering
 225+ // more than max_characters
 226+ cloak: false, // if true, then no visual updates of characters
 227+ // remaining (c_obj) occur
 228+ in_dom: false // if true and cloak == true, then number of characters
 229+ // remaining are stored as the attribute
 230+ // 'data-noblecount' of c_obj
 231+
 232+ };
 233+
 234+
 235+ //////////////////////////////////////////////////////////////////////////////////
 236+
 237+ // private functions and settings
 238+
 239+ /**********************************************************************************
 240+
 241+ FUNCTION
 242+ attach_nobility
 243+
 244+ DESCRIPTION
 245+ performs all initialization routines and display initiation
 246+
 247+ assigns both the keyup and keydown events to the target text entry
 248+ object; both keyup and keydown are used to provide the smoothest
 249+ user experience
 250+
 251+ if max_chars_passed via constructor
 252+ max_chars = max_chars_passed
 253+ else if number exists within counting_object (and number > 0)
 254+ max_chars = counting_object.number
 255+ else use default max_chars
 256+
 257+ PRE
 258+ t_obj and c_obj EXIST
 259+ c_settings and mc_passed initialized
 260+
 261+ POST
 262+ maximum number of characters for t_obj calculated and stored in max_char
 263+ key events attached to t_obj
 264+
 265+ **********************************************************************************/
 266+
 267+ function attach_nobility(t_obj, c_obj, c_settings, mc_passed){
 268+ var max_char = c_settings.max_chars;
 269+ var char_area = $(c_obj);
 270+
 271+ // first determine if max_char needs adjustment
 272+ if (!mc_passed) {
 273+ var tmp_num = char_area.text();
 274+ var isPosNumber = (/^[1-9]\d*$/).test(tmp_num);
 275+
 276+ if (isPosNumber) {
 277+ max_char = tmp_num;
 278+ }
 279+ }
 280+
 281+ // initialize display of characters remaining
 282+ // * note: initializing should not trigger on_update
 283+ event_internals(t_obj, char_area, c_settings, max_char, true);
 284+
 285+ // then attach the events -- seem to work better than keypress
 286+ $(t_obj).keydown(function(e) {
 287+ event_internals(t_obj, char_area, c_settings, max_char, false);
 288+
 289+ // to block text entry, return false
 290+ if (check_block_negative(e, t_obj, c_settings, max_char) == false) {
 291+ return false;
 292+ }
 293+ });
 294+
 295+ $(t_obj).keyup(function(e) {
 296+ event_internals(t_obj, char_area, c_settings, max_char, false);
 297+
 298+ // to block text entry, return false
 299+ if (check_block_negative(e, t_obj, c_settings, max_char) == false) {
 300+ return false;
 301+ }
 302+ });
 303+ }
 304+
 305+
 306+ /**********************************************************************************
 307+
 308+ FUNCTION
 309+ check_block_negative
 310+
 311+ DESCRIPTION
 312+ determines whether or not text entry within t_obj should be prevented
 313+
 314+ PRE
 315+ e EXISTS
 316+ t_obj VALID
 317+ c_settings and max_char initialized / calculated
 318+
 319+ POST
 320+ if t_obj text entry should be prevented FALSE is returned
 321+ otherwise TRUE returned
 322+
 323+ TODO
 324+ improve selection detection and permissible behaviors experience
 325+ ALSO
 326+ doesnt CURRENTLY block from the pasting of large chunks of text that
 327+ exceed max_char
 328+
 329+ **********************************************************************************/
 330+
 331+ function check_block_negative(e, t_obj, c_settings, max_char){
 332+ if (c_settings.block_negative) {
 333+ var char_code = e.which;
 334+ var selected;
 335+
 336+ // goofy handling required to work in both IE and FF
 337+ if (typeof document.selection != 'undefined') {
 338+ selected = (document.selection.createRange().text.length > 0);
 339+ } else {
 340+ selected = (t_obj[0].selectionStart != t_obj[0].selectionEnd);
 341+ }
 342+
 343+ //return false if can't write more
 344+ if ((!((find_remaining(t_obj, max_char) < 1) &&
 345+ (char_code > 47 || char_code == 32 || char_code == 0 || char_code == 13) &&
 346+ !e.ctrlKey &&
 347+ !e.altKey &&
 348+ !selected)) == false) {
 349+
 350+ // block text entry
 351+ return false;
 352+ }
 353+ }
 354+
 355+ // allow text entry
 356+ return true;
 357+ }
 358+
 359+
 360+ /**********************************************************************************
 361+
 362+ FUNCTION
 363+ find_remaining
 364+
 365+ DESCRIPTION
 366+ determines of the number of characters permitted (max_char), the number of
 367+ characters remaining until that limit has been reached
 368+
 369+ PRE
 370+ t_obj and max_char EXIST and are VALID
 371+
 372+ POST
 373+ returns integer of the difference between max_char and total number of
 374+ characters within the text entry object (t_obj)
 375+
 376+ **********************************************************************************/
 377+
 378+ function find_remaining(t_obj, max_char){
 379+ return max_char - ($(t_obj).val()).length;
 380+ }
 381+
 382+
 383+ /**********************************************************************************
 384+
 385+ FUNCTION
 386+ event_internals
 387+
 388+ DESCRIPTION
 389+ primarily used for the calculation of appropriate behavior resulting from
 390+ any event attached to the text entry object (t_obj)
 391+
 392+ whenever the char_rem and related display and/or DOM information needs
 393+ updating this function is called
 394+
 395+ if cloaking is being used, then no visual representation of the characters
 396+ remaining, nor attempt by this plugin to change any of its visual
 397+ characteristics will occur
 398+
 399+ if cloaking and in_dom are both TRUE, then the number of characters
 400+ remaining are stored within the HTML 5 compliant attribute of the
 401+ character count remaining object (c_obj) labeled 'data-noblecount'
 402+
 403+ PRE
 404+ c_settings, init_disp initialized
 405+
 406+ POST
 407+ performs all updates to the DOM visual and otherwise required
 408+ performs all relevant function calls
 409+
 410+ **********************************************************************************/
 411+
 412+ function event_internals(t_obj, char_area, c_settings, max_char, init_disp) {
 413+ var char_rem = find_remaining(t_obj, max_char);
 414+
 415+ // is chararacters remaining positive or negative
 416+ if (char_rem < 0) {
 417+ toggle_states(c_settings.on_negative, c_settings.on_positive, t_obj, char_area, c_settings, char_rem);
 418+ } else {
 419+ toggle_states(c_settings.on_positive, c_settings.on_negative, t_obj, char_area, c_settings, char_rem);
 420+ }
 421+
 422+ // determine whether or not to update the text of the char_area (or c_obj)
 423+ if (c_settings.cloak) {
 424+ // this slows stuff down quite a bit; TODO: implement better method of publically accessible data storage
 425+ if (c_settings.in_dom) {
 426+ char_area.attr('data-noblecount', char_rem);
 427+ }
 428+ } else {
 429+ // show the numbers of characters remaining
 430+ char_area.text(char_rem);
 431+ }
 432+
 433+ // if event_internals isn't being called for initialization purposes and
 434+ // on_update is a properly defined function then call it on this update
 435+ if (!init_disp && jQuery.isFunction(c_settings.on_update)) {
 436+ c_settings.on_update(t_obj, char_area, c_settings, char_rem);
 437+ }
 438+ }
 439+
 440+
 441+ /**********************************************************************************
 442+
 443+ FUNCTION
 444+ toggle_states
 445+
 446+ DESCRIPTION
 447+ performs the toggling operations between the watched positive and negative
 448+ characteristics
 449+
 450+ first, enables/triggers/executes the toggle_on behavior/class
 451+ second, disables the trigger_off class
 452+
 453+ PRE
 454+ toggle_on, toggle_off
 455+ IF DEFINED,
 456+ must be a string representation of a VALID class
 457+ OR
 458+ must be a VALID function
 459+
 460+ POST
 461+ toggle_on objects have been applied/executed
 462+ toggle_off class has been removed (if it is a class)
 463+
 464+ **********************************************************************************/
 465+
 466+ function toggle_states(toggle_on, toggle_off, t_obj, char_area, c_settings, char_rem){
 467+ if (toggle_on != null) {
 468+ if (typeof toggle_on == 'string') {
 469+ char_area.addClass(toggle_on);
 470+ } else if (jQuery.isFunction(toggle_on)) {
 471+ toggle_on(t_obj, char_area, c_settings, char_rem);
 472+ }
 473+ }
 474+
 475+ if (toggle_off != null) {
 476+ if (typeof toggle_off == 'string') {
 477+ char_area.removeClass(toggle_off);
 478+ }
 479+ }
 480+ }
 481+})(jQuery);
\ No newline at end of file
Index: trunk/extensions/TradeTrack/js/jquery.tipsy.js
@@ -0,0 +1,202 @@
 2+// tipsy, facebook style tooltips for jquery
 3+// version 1.0.0a
 4+// (c) 2008-2010 jason frame [jason@onehackoranother.com]
 5+// released under the MIT license
 6+
 7+(function($) {
 8+
 9+ function Tipsy(element, options) {
 10+ this.$element = $(element);
 11+ this.options = options;
 12+ this.enabled = true;
 13+ this.fixTitle();
 14+ }
 15+
 16+ Tipsy.prototype = {
 17+ show: function() {
 18+ var title = this.getTitle();
 19+ if (title && this.enabled) {
 20+ var $tip = this.tip();
 21+
 22+ $tip.find('.tipsy-inner')[this.options.html ? 'html' : 'text'](title);
 23+ $tip[0].className = 'tipsy'; // reset classname in case of dynamic gravity
 24+ $tip.remove().css({top: 0, left: 0, visibility: 'hidden', display: 'block'}).appendTo(document.body);
 25+
 26+ var pos = $.extend({}, this.$element.offset(), {
 27+ width: this.$element[0].offsetWidth,
 28+ height: this.$element[0].offsetHeight
 29+ });
 30+
 31+ var actualWidth = $tip[0].offsetWidth, actualHeight = $tip[0].offsetHeight;
 32+ var gravity = (typeof this.options.gravity == 'function')
 33+ ? this.options.gravity.call(this.$element[0])
 34+ : this.options.gravity;
 35+
 36+ var tp;
 37+ switch (gravity.charAt(0)) {
 38+ case 'n':
 39+ tp = {top: pos.top + pos.height + this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
 40+ break;
 41+ case 's':
 42+ tp = {top: pos.top - actualHeight - this.options.offset, left: pos.left + pos.width / 2 - actualWidth / 2};
 43+ break;
 44+ case 'e':
 45+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth - this.options.offset};
 46+ break;
 47+ case 'w':
 48+ tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width + this.options.offset};
 49+ break;
 50+ }
 51+
 52+ if (gravity.length == 2) {
 53+ if (gravity.charAt(1) == 'w') {
 54+ tp.left = pos.left + pos.width / 2 - 15;
 55+ } else {
 56+ tp.left = pos.left + pos.width / 2 - actualWidth + 15;
 57+ }
 58+ }
 59+
 60+ $tip.css(tp).addClass('tipsy-' + gravity);
 61+
 62+ if (this.options.fade) {
 63+ $tip.stop().css({opacity: 0, display: 'block', visibility: 'visible'}).animate({opacity: this.options.opacity});
 64+ } else {
 65+ $tip.css({visibility: 'visible', opacity: this.options.opacity});
 66+ }
 67+ }
 68+ },
 69+
 70+ hide: function() {
 71+ if (this.options.fade) {
 72+ this.tip().stop().fadeOut(function() { $(this).remove(); });
 73+ } else {
 74+ this.tip().remove();
 75+ }
 76+ },
 77+
 78+ fixTitle: function() {
 79+ var $e = this.$element;
 80+ if ($e.attr('title') || typeof($e.attr('original-title')) != 'string') {
 81+ $e.attr('original-title', $e.attr('title') || '').removeAttr('title');
 82+ }
 83+ },
 84+
 85+ getTitle: function() {
 86+ var title, $e = this.$element, o = this.options;
 87+ this.fixTitle();
 88+ var title, o = this.options;
 89+ if (typeof o.title == 'string') {
 90+ title = $e.attr(o.title == 'title' ? 'original-title' : o.title);
 91+ } else if (typeof o.title == 'function') {
 92+ title = o.title.call($e[0]);
 93+ }
 94+ title = ('' + title).replace(/(^\s*|\s*$)/, "");
 95+ return title || o.fallback;
 96+ },
 97+
 98+ tip: function() {
 99+ if (!this.$tip) {
 100+ this.$tip = $('<div class="tipsy"></div>').html('<div class="tipsy-arrow"></div><div class="tipsy-inner"></div>');
 101+ }
 102+ return this.$tip;
 103+ },
 104+
 105+ validate: function() {
 106+ if (!this.$element[0].parentNode) {
 107+ this.hide();
 108+ this.$element = null;
 109+ this.options = null;
 110+ }
 111+ },
 112+
 113+ enable: function() { this.enabled = true; },
 114+ disable: function() { this.enabled = false; },
 115+ toggleEnabled: function() { this.enabled = !this.enabled; }
 116+ };
 117+
 118+ $.fn.tipsy = function(options) {
 119+
 120+ if (options === true) {
 121+ return this.data('tipsy');
 122+ } else if (typeof options == 'string') {
 123+ var tipsy = this.data('tipsy');
 124+ if (tipsy) tipsy[options]();
 125+ return this;
 126+ }
 127+
 128+ options = $.extend({}, $.fn.tipsy.defaults, options);
 129+
 130+ function get(ele) {
 131+ var tipsy = $.data(ele, 'tipsy');
 132+ if (!tipsy) {
 133+ tipsy = new Tipsy(ele, $.fn.tipsy.elementOptions(ele, options));
 134+ $.data(ele, 'tipsy', tipsy);
 135+ }
 136+ return tipsy;
 137+ }
 138+
 139+ function enter() {
 140+ var tipsy = get(this);
 141+ tipsy.hoverState = 'in';
 142+ if (options.delayIn == 0) {
 143+ tipsy.show();
 144+ } else {
 145+ tipsy.fixTitle();
 146+ setTimeout(function() { if (tipsy.hoverState == 'in') tipsy.show(); }, options.delayIn);
 147+ }
 148+ };
 149+
 150+ function leave() {
 151+ var tipsy = get(this);
 152+ tipsy.hoverState = 'out';
 153+ if (options.delayOut == 0) {
 154+ tipsy.hide();
 155+ } else {
 156+ setTimeout(function() { if (tipsy.hoverState == 'out') tipsy.hide(); }, options.delayOut);
 157+ }
 158+ };
 159+
 160+ if (!options.live) this.each(function() { get(this); });
 161+
 162+ if (options.trigger != 'manual') {
 163+ var binder = options.live ? 'live' : 'bind',
 164+ eventIn = options.trigger == 'hover' ? 'mouseenter' : 'focus',
 165+ eventOut = options.trigger == 'hover' ? 'mouseleave' : 'blur';
 166+ this[binder](eventIn, enter)[binder](eventOut, leave);
 167+ }
 168+
 169+ return this;
 170+
 171+ };
 172+
 173+ $.fn.tipsy.defaults = {
 174+ delayIn: 0,
 175+ delayOut: 0,
 176+ fade: false,
 177+ fallback: '',
 178+ gravity: 'n',
 179+ html: false,
 180+ live: false,
 181+ offset: 0,
 182+ opacity: 0.8,
 183+ title: 'title',
 184+ trigger: 'hover'
 185+ };
 186+
 187+ // Overwrite this method to provide options on a per-element basis.
 188+ // For example, you could store the gravity in a 'tipsy-gravity' attribute:
 189+ // return $.extend({}, options, {gravity: $(ele).attr('tipsy-gravity') || 'n' });
 190+ // (remember - do not modify 'options' in place!)
 191+ $.fn.tipsy.elementOptions = function(ele, options) {
 192+ return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
 193+ };
 194+
 195+ $.fn.tipsy.autoNS = function() {
 196+ return $(this).offset().top > ($(document).scrollTop() + $(window).height() / 2) ? 's' : 'n';
 197+ };
 198+
 199+ $.fn.tipsy.autoWE = function() {
 200+ return $(this).offset().left > ($(document).scrollLeft() + $(window).width() / 2) ? 'e' : 'w';
 201+ };
 202+
 203+})(jQuery);
Index: trunk/extensions/TradeTrack/README
@@ -0,0 +1,45 @@
 2+STATUS:
 3+======
 4+
 5+This software is beta. There are missing features at this point.
 6+
 7+This software is specifically designed for use by the Wikimedia Foundation.
 8+It is a workflow system designed for external users to apply for the use of
 9+Wikimedia trademarks.
 10+
 11+
 12+INSTALLATION:
 13+============
 14+
 15+1. Rename this directory to extensions/TradeTrack inside your
 16+ MediaWiki directory.
 17+2. Add database tables from TradeTrack.sql using the sql.php MediaWiki tool.
 18+ (On Unix, if the current directory is the MediaWiki root directory, you can
 19+ say "php maintenance/sql.php extensions/TradeTrack/TradeTrack.sql".)
 20+ If you haven't created the AdminSettings.php file, you will have to do that
 21+ first; see http://www.mediawiki.org/wiki/Manual:AdminSettings.php
 22+ Alternatively, you can run the SQL file manually (you can use the command
 23+ "mysql -u $USER -p -e 'source TradeTrack.sql'" on Unix), but you might have to
 24+ edit it first, and replace the /*$wgDBprefix*/ and /*$wgDBTableOptions*/
 25+ strings with the corresponding settings.
 26+3. Add this line to the end of your LocalSettings.php:
 27+ require_once( "$IP/extensions/TradeTrack/TradeTrack.php" );
 28+4. You need to define three email addresses in LocalSettings.php after that:
 29+
 30+ $wgTradeTrackEmailCommercial = "foo@bar.com";
 31+ $wgTradeTrackEmailNonCommercial = "foo@bar.com";
 32+ $wgTradeTrackEmailMedia = "foo@bar.com";
 33+
 34+
 35+CONTACT:
 36+=======
 37+
 38+* Brandon Harris
 39+* bharris@wikimedia.org
 40+* jorm in #mediawiki on irc.freenode.net
 41+
 42+CREDITS:
 43+=======
 44+
 45+Written by Brandon Harris for the Wikimedia Foundation.
 46+
Index: trunk/extensions/TradeTrack/templates/TradeTrackEmail.php
@@ -0,0 +1,42 @@
 2+<?php
 3+class TradeTrackEmail extends QuickTemplate { public function execute() { ?>
 4+
 5+<?php
 6+ /**
 7+ * Note that this email does NOT use i18n. The email template is in English.
 8+ *
 9+ * The reason for this is that the people who will be recieving these emails
 10+ * expect them in English, and MediaWiki will happily translate it to French
 11+ * if the user submitting the trademark request is doing so in French.
 12+ *
 13+ * Hence, English here.
 14+ *
 15+ */
 16+
 17+?>
 18+
 19+ A new request to utilize a Wikimedia trademark has arrived.
 20+
 21+ Purpose: <?php echo $this->data['tData']['purpose'] ?>
 22+ <?php if ( $this->data['tData']['agreementType'] ) { ?>Agreement Type: <?php echo $this->data['tData']['agreementType'] ?><?php } ?>
 23+
 24+ Usage: <?php echo $this->data['tData']['usage'] ?>
 25+
 26+ Marks:<?php foreach ( $this->data['tData']['TRADEMARK_LIST'] as $trademark ) {
 27+ if ( ( isset( $this->data['tData']['trademarks'] ) )
 28+ && ( in_array( $trademark, $this->data['tData']['trademarks'] ) ) ) {
 29+ echo wfMsg( "tradetrack-which-$trademark" );
 30+ if ( $trademark == 'other' ) {
 31+ echo $this->data['tData']['otherval'];
 32+ }
 33+ }
 34+ } ?>
 35+ Mailing Address:
 36+ <?php echo $this->data['tData']['mailingaddress'] ?>
 37+
 38+ Name: <?php echo $this->data['tData']['name'] ?>
 39+ Organization Name: <?php echo $this->data['tData']['orgname'] ?>
 40+ Email: <?php echo $this->data['tData']['email'] ?>
 41+ Phone: <?php echo $this->data['tData']['phone'] ?>
 42+
 43+<?php } }
\ No newline at end of file
Index: trunk/extensions/TradeTrack/templates/TradeTrackScreenRouting.php
@@ -0,0 +1,37 @@
 2+<?php
 3+
 4+class TradeTrackScreenRouting extends TradeTrackScreen { public function execute() { ?>
 5+
 6+<?php echo wfMsg( 'tradetrack-overview' ) ?>
 7+
 8+
 9+<form method="post" action="<?php echo $this->data['tData']['formURL'] ?>" class="tradetrack-master" id="tradetrack-form">
 10+ <input type="hidden" name="doaction" value="route" />
 11+ <div id="tradetrack-screens">
 12+
 13+ <p class="tradetrack-f-r"><?php echo wfMsg( 'tradetrack-all-fields-required' ) ?></p>
 14+ <?php if ( $this->data['errors'] ) { ?>
 15+ <div class="tradetrack-errornotice">
 16+ <?php echo wfMsg( 'tradetrack-errors-have-happened' ) ?>
 17+ <?php $this->showErrors( 'global' ) ?>
 18+ </div>
 19+ <?php } ?>
 20+ <div class="<?php echo ( $this->hasError( 'tradetrack-purpose' ) ? 'tradetrack-element-error' : 'tradetrack-element' ) ?>">
 21+ <label class="tradetrack-question-label"><?php echo wfMsg( 'tradetrack-purpose-question' ) ?></label><br style="clear:both" />
 22+ <?php $this->showErrors( 'tradetrack-purpose' ) ?>
 23+ <ul class="tradetrack-element-list">
 24+ <li><input type="radio" name="tradetrack-purpose" value="Commercial" />
 25+ <?php echo wfMsg( 'tradetrack-purpose-label-commercial' ) ?> <?php echo wfMsg( 'tradetrack-purpose-expanse-commercial' ) ?></li>
 26+ <li><input type="radio" name="tradetrack-purpose" value="Non-Commercial" />
 27+ <?php echo wfMsg( 'tradetrack-purpose-label-noncommercial' ) ?> <?php echo wfMsg( 'tradetrack-purpose-expanse-noncommercial' ) ?></li>
 28+ <li><input type="radio" name="tradetrack-purpose" value="Media" />
 29+ <?php echo wfMsg( 'tradetrack-purpose-label-media' ) ?> <?php echo wfMsg( 'tradetrack-purpose-expanse-media' ) ?></li>
 30+ </ul>
 31+ </div>
 32+ <div class="tradetrack-button-box">
 33+ <button type="submit" class="tradetrack-button"><?php echo wfMsg( 'tradetrack-button-continue' ) ?></button>
 34+ </div> <!-- buttonbox -->
 35+ </div> <!-- close screens -->
 36+</form>
 37+
 38+<?php } }
\ No newline at end of file
Index: trunk/extensions/TradeTrack/templates/TradeTrackScreenThanks.php
@@ -0,0 +1,8 @@
 2+<?php
 3+
 4+class TradeTrackScreenThanks extends TradeTrackScreen { public function execute() { ?>
 5+
 6+<?php echo wfMsg( 'tradetrack-thanks-text' ) ?>
 7+
 8+
 9+<?php } }
\ No newline at end of file
Index: trunk/extensions/TradeTrack/templates/TradeTrackScreenDetailsForm.php
@@ -0,0 +1,116 @@
 2+<?php
 3+class TradeTrackScreenDetailsForm extends TradeTrackScreen { public function execute() { ?>
 4+
 5+<?php echo wfMsg( 'tradetrack-overview' ) ?>
 6+
 7+<form method="post" action="<?php echo $this->data['tData']['formURL'] ?>" class="tradetrack-master" id="tradetrack-form">
 8+ <input type="hidden" name="doaction" value="details" />
 9+ <input type="hidden" name="tradetrack-purpose" value="<?php echo $this->data['tData']['purpose'] ?>" />
 10+ <?php if ( $this->data['tData']['agreementType'] ) { ?>
 11+ <input type="hidden" name="tradetrack-elements-agreement" value="<?php echo $this->data['tData']['agreementType'] ?>" />
 12+ <?php } ?>
 13+
 14+ <div id="tradetrack-screens">
 15+ <p class="tradetrack-f-r"><?php echo wfMsg( 'tradetrack-all-fields-required' ) ?></p>
 16+ <?php if ( $this->data['errors'] ) { ?>
 17+ <div class="tradetrack-errornotice">
 18+ <?php echo wfMsg( 'tradetrack-errors-have-happened' ) ?>
 19+ <?php $this->showErrors( 'global' ) ?>
 20+ </div>
 21+ <?php } ?>
 22+
 23+ <div class="<?php echo ( $this->hasError( 'tradetrack-elements-usage' ) ? 'tradetrack-element-error' : 'tradetrack-element' ) ?>">
 24+ <label class="tradetrack-question-label"><?php echo wfMsg( 'tradetrack-usage-label' ) ?></label>
 25+ <span class="tradetrack-field-hint"
 26+ title="<?php echo wfMsg( 'tradetrack-usage-expanse' ) ?>"
 27+ original-title="<?php echo wfMsg( 'tradetrack-usage-expanse' ) ?>"></span><br style="clear:both" />
 28+ <?php $this->showErrors( 'tradetrack-elements-usage' ) ?>
 29+ <textarea rows="5" id="tradetrack-elements-usage-textarea" name="tradetrack-elements-usage"><?php echo $this->data['tData']['usage'] ?></textarea><br />
 30+ <div class="characters-remaining-box"><span id="tradetrack-elements-usage-count"></span> <?php echo wfMsg( 'tradetrack-characters-remaining-notice' ); ?></div>
 31+ </div>
 32+ <div class="<?php echo ( ( ( $this->hasError( 'tradetrack-element-list' ) ) || ( $this->hasError( 'tradetrack-elements-otherval' ) ) ) ? 'tradetrack-element-error' : 'tradetrack-element' ) ?>">
 33+ <label class="tradetrack-question-label"><?php echo wfMsg( 'tradetrack-logo-which' ) ?></label><br />
 34+ <?php $this->showErrors( 'tradetrack-element-list' ) ?>
 35+ <?php $this->showErrors( 'tradetrack-elements-otherval' ) ?>
 36+ <ul class="tradetrack-element-list">
 37+ <?php foreach ( $this->data['tData']['TRADEMARK_LIST'] as $trademark ) { ?>
 38+
 39+ <li><input type="checkbox" name="tradetrack-which-<?php echo $trademark ?>" value="true"
 40+ <?php if ( ( isset( $this->data['tData']['trademarks'] ) )
 41+ && ( in_array( $trademark, $this->data['tData']['trademarks'] ) ) ) { ?>
 42+ checked="checked"
 43+ <?php } ?>
 44+
 45+ /><?php echo wfMsg( "tradetrack-which-$trademark" ) ?>
 46+ <?php if ( $trademark == 'other' ) { ?>
 47+ <input type="text" name="tradetrack-elements-otherval" maxlength="200" value="<?php echo $this->data['tData']['otherval'] ?>" />
 48+ <?php } ?>
 49+ </li>
 50+ <?php } ?>
 51+ </ul>
 52+ </div>
 53+
 54+ <div class="<?php echo ( $this->hasError( 'tradetrack-elements-mailingaddress' ) ? 'tradetrack-element-error' : 'tradetrack-element' ) ?>">
 55+ <label class="tradetrack-question-label"><?php echo wfMsg( 'tradetrack-about-label-mailingaddress' ) ?></label>
 56+ <span class="tradetrack-field-hint"
 57+ title="<?php echo wfMsg( 'tradetrack-about-expanse-mailingaddress' ) ?>"
 58+ original-title="<?php echo wfMsg( 'tradetrack-about-expanse-mailingaddress' ) ?>"></span><br style="clear:both" />
 59+ <?php $this->showErrors( 'tradetrack-elements-mailingaddress' ) ?>
 60+ <textarea rows="5" id="tradetrack-elements-mailingaddress-textarea" name="tradetrack-elements-mailingaddress"><?php echo $this->data['tData']['mailingaddress'] ?></textarea><br />
 61+ <div class="characters-remaining-box"><span id="tradetrack-elements-mailingaddress-count"></span> <?php echo wfMsg( 'tradetrack-characters-remaining-notice' ); ?></div>
 62+ </div>
 63+ <div class="<?php echo ( $this->hasError( 'tradetrack-elements-name' ) ? 'tradetrack-element-error' : 'tradetrack-element' ) ?>">
 64+ <label class="tradetrack-question-label"><?php echo wfMsg( 'tradetrack-about-label-yourname' ) ?></label><br />
 65+ <?php $this->showErrors( 'tradetrack-elements-name' ) ?>
 66+ <input type="text" name="tradetrack-elements-name" maxlength="200" value="<?php echo $this->data['tData']['name'] ?>" /><br />
 67+ </div>
 68+ <div class="<?php echo ( $this->hasError( 'tradetrack-elements-orgname' ) ? 'tradetrack-element-error' : 'tradetrack-element' ) ?>">
 69+ <label class="tradetrack-question-label"><?php echo wfMsg( 'tradetrack-about-label-orgname' ) ?></label>
 70+ <span class="tradetrack-field-hint"
 71+ title="<?php echo wfMsg( 'tradetrack-about-expanse-orgname' ) ?>"
 72+ original-title="<?php echo wfMsg( 'tradetrack-about-expanse-orgname' ) ?>"></span><br style="clear:both" />
 73+ <?php $this->showErrors( 'tradetrack-elements-orgname' ) ?>
 74+ <input type="text" name="tradetrack-elements-orgname" maxlength="200" value="<?php echo $this->data['tData']['orgname'] ?>" /><br />
 75+ </div>
 76+ <div class="<?php echo ( $this->hasError( 'tradetrack-elements-email' ) ? 'tradetrack-element-error' : 'tradetrack-element' ) ?>">
 77+ <label class="tradetrack-question-label"><?php echo wfMsg( 'tradetrack-about-label-email' ) ?></label>
 78+ <span class="tradetrack-field-hint"
 79+ title="<?php echo wfMsg( 'tradetrack-about-expanse-email' ) ?>"
 80+ original-title="<?php echo wfMsg( 'tradetrack-about-expanse-email' ) ?>"></span><br style="clear:both" />
 81+ <?php $this->showErrors( 'tradetrack-elements-email' ) ?>
 82+ <input type="text" name="tradetrack-elements-email" maxlength="200" value="<?php echo $this->data['tData']['email'] ?>" /><br />
 83+ </div>
 84+ <div class="<?php echo ( $this->hasError( 'tradetrack-elements-confirmemail' ) ? 'tradetrack-element-error' : 'tradetrack-element' ) ?>">
 85+ <label class="tradetrack-question-label"><?php echo wfMsg( 'tradetrack-about-label-confirmemail' ) ?></label>
 86+ <span class="tradetrack-field-hint"
 87+ title="<?php echo wfMsg( 'tradetrack-about-expanse-confirmemail' ) ?>"
 88+ original-title="<?php echo wfMsg( 'tradetrack-about-expanse-confirmemail' ) ?>"></span><br style="clear:both" />
 89+ <?php $this->showErrors( 'tradetrack-elements-confirmemail' ) ?>
 90+ <input type="text" name="tradetrack-elements-confirmemail" maxlength="200" value="<?php echo $this->data['tData']['confirmemail'] ?>" /><br />
 91+ </div>
 92+ <div class="<?php echo ( $this->hasError( 'tradetrack-elements-phone' ) ? 'tradetrack-element-error' : 'tradetrack-element' ) ?>">
 93+ <label class="tradetrack-question-label"><?php echo wfMsg( 'tradetrack-about-label-phone' ) ?></label><br style="clear:both" />
 94+ <?php $this->showErrors( 'tradetrack-elements-phone' ) ?>
 95+ <input type="text" name="tradetrack-elements-phone" maxlength="200" value="<?php echo $this->data['tData']['phone'] ?>" /><br />
 96+ </div>
 97+ <div class="<?php echo ( $this->hasError( 'tradetrack-statement-value' ) ? 'tradetrack-element-error' : 'tradetrack-element' ) ?>">
 98+ <label class="tradetrack-question-label"><?php echo wfMsg( 'tradetrack-statement-label' ) ?></label><br />
 99+ <span class="tradetrack-question-expanse"><?php echo wfMsg( 'tradetrack-statement-expanse' ) ?></span><br />
 100+ <?php $this->showErrors( 'tradetrack-statement-value' ) ?>
 101+ <ul class="tradetrack-element-list">
 102+ <li><input type="checkbox"
 103+ name="tradetrack-elements-statementagreement"
 104+ value="true"
 105+ <?php echo ( $this->data['tData']['statementagreement'] == "true" ) ? 'checked="checked"' : "" ?>
 106+ />
 107+ <?php echo wfMsg( 'tradetrack-statement-checkboxlabel' ) ?>
 108+ </li>
 109+ </ul>
 110+ </div>
 111+ <div class="tradetrack-button-box">
 112+ <button type="submit" class="tradetrack-button"><?php echo wfMsg( 'tradetrack-button-submit' ) ?></button>
 113+ </div> <!-- buttonbox -->
 114+ </div> <!-- close screen -->
 115+</form>
 116+
 117+<?php } }
\ No newline at end of file
Index: trunk/extensions/TradeTrack/templates/TradeTrackScreen.php
@@ -0,0 +1,48 @@
 2+<?php
 3+
 4+/**
 5+ * This class extends QuickTemplate, adding two things:
 6+ * 1) A constructor that takes a data array as an argument (rather than requiring it to be set later)
 7+ * 2) Adds a method for displaying an array of error strings, given a target field.
 8+ *
 9+ * @author bharris
 10+ */
 11+abstract class TradeTrackScreen extends QuickTemplate {
 12+
 13+ public function __contruct( array $data ) {
 14+ $this->data = $data;
 15+ }
 16+
 17+ /**
 18+ * This displays a list of error messages for a single field.
 19+ *
 20+ * @param $target The target field for the errors.
 21+ */
 22+ public function showErrors( $target ) {
 23+ $errors = $this->data['errors'];
 24+ if ( ( isset( $errors ) ) && ( isset( $errors[$target] ) ) ) {
 25+ $eList = $errors[$target];
 26+ $theHTML = '<ul class="tradetrack-errors">';
 27+ foreach ( $eList as $e ) {
 28+ $theHTML .= '<li>' . $e . '</li>';
 29+ }
 30+ $theHTML .= '</ul>';
 31+ echo $theHTML;
 32+ }
 33+ return;
 34+ }
 35+ /**
 36+ * Tests whether or not a specified target has an error.
 37+ *
 38+ * @param $target The target field
 39+ * @return boolean true or false, depending.
 40+ */
 41+ public function hasError ( $target ) {
 42+ $errors = $this->data['errors'];
 43+ if ( ( isset( $errors ) ) && ( isset( $errors[$target] ) ) ) {
 44+ return true;
 45+ }
 46+ return false;
 47+ }
 48+
 49+}
\ No newline at end of file
Index: trunk/extensions/TradeTrack/templates/TradeTrackScreenNonComAgreement.php
@@ -0,0 +1,41 @@
 2+<?php
 3+class TradeTrackScreenNonComAgreement extends TradeTrackScreen { public function execute() { ?>
 4+
 5+<?php echo wfMsg( 'tradetrack-overview' ) ?>
 6+
 7+<form method="post" action="<?php echo $this->data['tData']['formURL'] ?>" class="tradetrack-master" id="tradetrack-form">
 8+ <input type="hidden" name="doaction" value="noncomroute" />
 9+ <input type="hidden" name="tradetrack-purpose" value="<?php echo $this->data['tData']['purpose'] ?>" />
 10+
 11+ <div id="tradetrack-screens">
 12+
 13+ <p class="tradetrack-f-r"><?php echo wfMsg( 'tradetrack-all-fields-required' ) ?></p>
 14+ <?php if ( $this->data['errors'] ) { ?>
 15+ <div class="tradetrack-errornotice">
 16+ <?php echo wfMsg( 'tradetrack-errors-have-happened' ) ?>
 17+ <?php $this->showErrors( 'global' ) ?>
 18+ </div>
 19+ <?php } ?>
 20+ <div class="<?php echo ( $this->hasError( 'tradetrack-elements-agreement' ) ? 'tradetrack-element-error' : 'tradetrack-element' ) ?>">
 21+
 22+ <label class="tradetrack-question-label"><?php echo wfMsg( 'tradetrack-nonprofit-preexisting-agreement-question' ) ?></label>
 23+ <br style="clear:both" />
 24+ <?php $this->showErrors( 'tradetrack-elements-agreement' ) ?>
 25+
 26+ <ul class="tradetrack-element-list">
 27+ <li><input type="radio" name="tradetrack-elements-agreement" value="Yes" />
 28+ <?php echo wfMsg( 'tradetrack-nonprofit-preexisting-agreement-yes' ) ?></li>
 29+ <li><input type="radio" name="tradetrack-elements-agreement" value="No" />
 30+ <?php echo wfMsg( 'tradetrack-nonprofit-preexisting-agreement-no-unaffilliated' ) ?></li>
 31+ <li><input type="radio" name="tradetrack-elements-agreement" value="Mistake" />
 32+ <?php echo wfMsg( 'tradetrack-nonprofit-preexisting-agreement-no-mistake' ) ?></li>
 33+ </ul>
 34+ </div>
 35+ <div class="tradetrack-button-box">
 36+ <button id="tradetrack-elements-back" class="tradetrack-button"><?php echo wfMsg( 'tradetrack-button-back' ) ?></button>
 37+ <button type="submit" class="tradetrack-button"><?php echo wfMsg( 'tradetrack-button-continue' ) ?></button>
 38+ </div> <!-- buttonbox -->
 39+ </div> <!-- close screens -->
 40+</form>
 41+
 42+<?php } }
\ No newline at end of file

Comments

#Comment by Siebrand (talk | contribs)   01:34, 7 October 2010

My compliments and thanks for the time you took to add the message documentation, Brandon!

#Comment by Raymond (talk | contribs)   07:04, 7 October 2010

Seen during local i18n review:

Undefined variable: query in D:\F_Programmierung\xampp\htdocs\wiki2\extensions\TradeTrack\SpecialTradeTrack.php on line 225

#Comment by Raymond (talk | contribs)   07:19, 7 October 2010

Olala, some more:

Notice: Undefined index: agreementType in \TradeTrack\templates\TradeTrackScreenDetailsForm.php on line 9

Notice: Undefined index: usage in \TradeTrack\templates\TradeTrackScreenDetailsForm.php on line 28

Notice: Undefined index: otherval in \TradeTrack\templates\TradeTrackScreenDetailsForm.php on line 46

Notice: Undefined index: mailingaddress in \TradeTrack\templates\TradeTrackScreenDetailsForm.php on line 59

and for all other address fields.


#Comment by 😂 (talk | contribs)   22:52, 7 October 2010

QuickTemplate? *puke*

#Comment by Trevor Parscal (WMF) (talk | contribs)   22:01, 20 October 2010

Maybe it could use some refactoring, but please consider what it looked like before QuickTemplate was used - I know that code is not checked in, but the XML class wasn't doing us any favors here.

#Comment by MZMcBride (talk | contribs)   22:10, 20 October 2010

I think includes/HTMLForm.php would probably clean this up quite a bit, but that might require making a few changes to HTMLForm.php.

As long as this code works, I don't see an issue. It might be nice to throw a "to-do" or "fixme" comment in the file for later, though.

#Comment by 😂 (talk | contribs)   23:21, 20 October 2010

Looks like a good suggestion to me (I realize my comment above was unhelpful, but I agree it's not a blocking issue and shouldn't cause the fixme here).

In general, I think we should use HTMLForm more. It's awesome :)

#Comment by Reedy (talk | contribs)   14:52, 4 November 2010
$eList = $errorsArray[$target];

$errorsArray is undefined

#Comment by Jorm (WMF) (talk | contribs)   20:32, 2 February 2011

This extension is not deployed and not needed for 1.17.

#Comment by RobLa-WMF (talk | contribs)   20:33, 2 February 2011

Adding 'nodeploy' tag then. Thanks!

#Comment by MZMcBride (talk | contribs)   04:50, 3 February 2011

Probably safe to remove the "fixme" here and put the issues in a bug.

#Comment by 😂 (talk | contribs)   18:42, 13 July 2011

Agreed, initial issues with the commit were resolved in followups. Ideas for improvement/expansion can go in BZ.

Status & tagging log