Compare commits

...

63 Commits

Author SHA1 Message Date
lealife
d58fd6434f gen tmp tool without revel 2021-08-15 10:35:34 +08:00
lealife
0f9733c890 fix note history xss 2021-03-27 16:12:46 +08:00
lealife
14643d3cf4 fix doc nav scroll 2021-03-26 16:07:26 +08:00
lealife
d49f837b2e fix paste image twice 2021-03-26 16:01:42 +08:00
lealife
cb19d235c6 Update .travis.yml 2021-03-26 15:52:35 +08:00
lealife
d41d1d8a34 Update README.md 2021-03-26 15:36:40 +08:00
lealife
b6d9c7816c update conf 2021-03-26 15:36:38 +08:00
lealife
7345d065b8 Update run.sh 2021-03-26 15:05:37 +08:00
life
3211e8607d Merge pull request #960 from kant/patch-11
Typos fixed on lines 09, 49, 57, 170
2021-03-26 15:04:07 +08:00
life
be3e0fa2c2 Merge pull request #959 from kant/patch-9
Update untranslated lines
2021-03-26 15:03:55 +08:00
life
41d24fe134 Merge pull request #958 from kant/patch-10
Syntax issues (on line 26)
2021-03-26 15:03:44 +08:00
life
e9f7141f65 Merge pull request #957 from kant/patch-8
Typos fixed on lines 19, 25
2021-03-26 15:03:31 +08:00
life
d7e60e1e23 Merge pull request #956 from kant/patch-7
Typos fixed on lines 05, 27
2021-03-26 15:03:18 +08:00
life
1ed6410380 Merge pull request #955 from kant/patch-6
Typos fixed on lines 45, 79, 84
2021-03-26 15:02:55 +08:00
life
42783f8886 Merge pull request #938 from myadream/hotfix-mongo-replic
修复截取mongo数据库名称截取不全
2021-03-26 15:02:23 +08:00
lealife
80054e8aa9 ignore 2021-03-26 14:28:48 +08:00
lealife
7be4f10441 shell 2021-03-26 14:25:03 +08:00
lealife
a0a8b57992 Update .travis.yml 2021-03-26 14:23:38 +08:00
lealife
e8ee0862ef delete leanote.tar.gz 2021-03-26 14:22:19 +08:00
lealife
1b006b83ad use revel 1.0.0 && go module 2021-03-26 14:19:53 +08:00
Darío Hereñú
71527eab2b Typos fixed on lines 09, 49, 57, 170 2020-10-21 17:36:10 -03:00
Darío Hereñú
1576c57241 Syntax issues (on line 26)
Typo fixed on line 20, 73, 83, 94, 104, 146, 195, 199, 214, 245
2020-10-21 17:28:17 -03:00
Darío Hereñú
187d602c91 Update untranslated lines
* Typos fixed on lines 14, 35, 44, 81
2020-10-21 17:16:32 -03:00
Darío Hereñú
e88cd2d880 Typos fixed on lines 19, 25 2020-10-21 17:07:29 -03:00
Darío Hereñú
4baea3e63f Typos fixed on lines 05, 27 2020-10-21 17:00:51 -03:00
Darío Hereñú
eeed7ad847 Typos fixed on lines 45, 79, 84 2020-10-21 16:47:53 -03:00
高健
9f27939471 修复截取mongo数据库名称截取不全 2020-03-11 18:52:15 +08:00
lealife
5e61291703 searchreplace toolbar 2019-04-09 17:44:44 +08:00
lealife
59174b40ff Fix update noteContent only don't incr Usn
https://github.com/leanote/leanote/issues/865
2019-03-28 14:27:41 +08:00
life
359c768041 Merge pull request #869 from vincentruan/master
add something should be ignored with IDE(goland) to .gitignore
2019-03-24 20:54:37 +08:00
life
a64861ce63 Merge pull request #809 from pulkitsethi/patch-1
Fix link to iOS app store on README.md
2019-03-24 20:54:10 +08:00
vincentruan
115d50f793 Merge branch 'master' of github.com:vincentruan/leanote 2019-03-24 19:03:31 +08:00
lealife
e4b5856338 fix paste image twice 2018-12-07 17:40:38 +08:00
Pulkit Sethi
a8cbfb1ec0 Fix link to iOS app store on README.md
Link to iOS app store was broken
2018-08-15 11:51:41 -04:00
lealife
c4bb20fd12 ace editor bug fixed 2018-06-13 16:37:10 +08:00
lealife
5eba524bfc markdown 2018-03-28 14:45:22 +08:00
vincentruan
0ba46039a2 ignore idea 2018-03-09 17:35:21 +08:00
lealife
340df9ce16 release 2.6.1 for bind all ip 2018-03-08 11:21:05 +08:00
lealife
eaa15d2905 listen on all ip addresses 2018-03-08 10:49:55 +08:00
lealife
29413c0e52 debug 2017-12-01 10:52:48 +08:00
lealife
347e79610e add vendor agtorre/gocolorize 2017-12-01 10:52:41 +08:00
lealife
c084792d32 prepare to release 2.6 2017-12-01 10:25:24 +08:00
lealife
d77e636e53 remove unused 2017-12-01 10:25:08 +08:00
lealife
4729252c63 vendor 2017-11-30 20:18:21 +08:00
lealife
6cbb82a927 cmd 2017-11-30 20:15:28 +08:00
lealife
bba9030f14 cmd 2017-11-30 20:11:10 +08:00
lealife
7cf6dbbe38 remove 2017-11-30 19:57:47 +08:00
lealife
00993e7fe1 Merge branch 'go-vendor-v2' 2017-11-30 19:55:54 +08:00
lealife
0fb92efbf3 go vendor 2017-11-30 19:55:33 +08:00
lealife
2856da6888 update travis 2017-11-30 19:14:42 +08:00
life
c01a6dc9e9 Merge pull request #687 from wangduanduan/master
修改网页端博客文档导航无效的bug
2017-11-30 18:54:36 +08:00
lealife
845c96cd48 update travis 2017-11-30 18:50:44 +08:00
lealife
094d18be46 remove deprecated log 2017-11-30 18:46:30 +08:00
lealife
c6937fd184 Compatible with revel 0.18 2017-11-30 18:10:59 +08:00
lealife
430744a324 build 2017-11-30 12:23:30 +08:00
lealife
98c1589a1a search replace tinymce 2017-11-30 12:23:30 +08:00
wang duanduan
d81bb8c893 修改网页端博客文档导航无效的bug 2017-11-10 13:38:33 +08:00
life
21b774143b Merge pull request #641 from jjchern/patch-1
Fix a few typos in readme
2017-08-09 11:11:03 +08:00
jjchern
3925adc3f2 Fix a few typos in readme 2017-08-01 12:58:19 -05:00
lealife
371f5e23c3 realease 2017-07-27 20:25:54 +08:00
lealife
825fda5544 windows export pdf 2017-07-27 20:00:53 +08:00
lealife
6ede5c1559 for release 2017-07-27 15:31:52 +08:00
lealife
1ff90dacde Fix windows export pdf
https://github.com/leanote/leanote/issues/514
2017-07-27 15:08:34 +08:00
109 changed files with 3014 additions and 1906 deletions

13
.gitignore vendored
View File

@@ -1,3 +1,7 @@
test-results/
tmp/
routes/
/none
/src /src
pkg pkg
bin2 bin2
@@ -7,7 +11,6 @@ bin/release
bin/test.sh bin/test.sh
bin/tmp bin/tmp
bin/test bin/test
bin/src
public/upload public/upload
app/routes/routes.go app/routes/routes.go
app/tmp/main.go app/tmp/main.go
@@ -15,5 +18,11 @@ app/tmp/main.go
.settings .settings
.project .project
public/config.codekit public/config.codekit
files files/
/node_modules /node_modules
.idea
*.iml
target/
package/leanote.tar.gz
package/leanote/
leanote.log

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>leanote-public</name>
<comment>leanote, your own cloud note!</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>com.googlecode.goclipse.goBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>goclipse.goNature</nature>
</natures>
</projectDescription>

View File

@@ -1,29 +1,41 @@
language: go language: go
go: 1.7 go: 1.15
services: services:
- mongodb # 2.4.12 - mongodb # 2.4.12
install: install:
- go version
- export PATH=$PATH:$HOME/gopath/bin - export PATH=$PATH:$HOME/gopath/bin
- go get -v github.com/leanote/leanote/app # - go get -v github.com/leanote/leanote/app
- go get -u github.com/revel/cmd/revel - go get -u github.com/revel/cmd/revel
- ls $GOPATH/src/github.com/revel/ # - ls $GOPATH/src/github.com/revel/
# - go get github.com/revel/moudle/revel # - go get github.com/revel/moudle/revel
# - go install github.com/revel/cmd/revel # - go install github.com/revel/cmd/revel
- revel version
- pwd - pwd
- ls - ls
script: script:
- wget https://github.com/leanote/leanote/archive/refs/heads/master.zip
- unzip master.zip
- mv leanote-master leanote
- cd leanote
- mongo --version - mongo --version
- mongorestore -h localhost -d leanote --directoryperdb ./mongodb_backup/leanote_install_data/ - mongorestore -h localhost -d leanote --dir ./mongodb_backup/leanote_install_data/
- cd ./sh
# - cd $GOPATH/src/github.com/leanote/leanote/sh
- sh run.sh &
# gen tmp/main.go, routes/routes.go # gen tmp/main.go, routes/routes.go
- go run app/cmd/main.go #- go run app/cmd/main.go
# build # build
- go build -o leanote github.com/leanote/leanote/app/tmp #- go build -o leanote github.com/leanote/leanote/app/tmp
# run with port 9000 # run with port 9000
- ./leanote -importPath=github.com/leanote/leanote -runMode=dev -port=9000 & #- ./leanote -importPath=github.com/leanote/leanote -runMode=dev -port=9000 &
- sleep 10s; - sleep 10s;
# test # test
- curl http://localhost:9000 - curl http://localhost:9000

359
LICENSE
View File

@@ -1,359 +0,0 @@
LEANOTE - NOT JUST A NOTEPAD!
Copyright 2014-2015 by the contributors
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
Leanote is licensed under the GPL v2.
life(life@leanote.com, lifephp@gmail.com)
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{description}
Copyright (C) {year} {fullname}
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
{signature of Ty Coon}, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@@ -31,7 +31,7 @@ We have been using the popular note-taking software/service `Evernote` as our kn
* A more powerful editor: `Evernote`'s editor lacks the functionalities of **document navigation**, **syntax based code rendering** (as a programmer, syntax highlighted code rendering is a necessity), **image resizing** and so forth. * A more powerful editor: `Evernote`'s editor lacks the functionalities of **document navigation**, **syntax based code rendering** (as a programmer, syntax highlighted code rendering is a necessity), **image resizing** and so forth.
* Everybody loves *markdown*, however `Evernote` simply wouldn't add it despite of years' of requests from users. So we will do the favor and bring a *markdown* enabled editor to you, guess what, it is also rendered in real-time! * Everybody loves *markdown*, however `Evernote` simply wouldn't add it despite of years' of requests from users. So we will do the favor and bring a *markdown* enabled editor to you, guess what, it is also rendered in real-time!
* If you a developer and miss the `Vim` or `Emacs` ways of writing, we offer you the choice of `Vim` and `Emacs` editting modes. Equipped with *markdown* syntax for text formatting, you will never need to touch your mouse while writing. * If you a developer and miss the `Vim` or `Emacs` ways of writing, we offer you the choice of `Vim` and `Emacs` editing modes. Equipped with *markdown* syntax for text formatting, you will never need to touch your mouse while writing.
* We love managing knowledge and thoughts as much as sharing them, so everybody has their own note account (`Evernote`, `Onenote`, `Google doc`, `Wiz note` etc.) and social media account (`Facebook`, `Wordpress`, blogs, etc.). But why cant those two be one? Leanote makes this first step to bridge the private note-taking and public knowledge sharing seamlessly. * We love managing knowledge and thoughts as much as sharing them, so everybody has their own note account (`Evernote`, `Onenote`, `Google doc`, `Wiz note` etc.) and social media account (`Facebook`, `Wordpress`, blogs, etc.). But why cant those two be one? Leanote makes this first step to bridge the private note-taking and public knowledge sharing seamlessly.
* A complete and all-platform (sorry Windows phone) covering software suite: that includes Leanote Web & Server (this repository), [Desktop app](https://github.com/leanote/desktop-app), [iOS](https://github.com/leanote/leanote-ios), [Android](https://github.com/leanote/leanote-android). And they are all open source! * A complete and all-platform (sorry Windows phone) covering software suite: that includes Leanote Web & Server (this repository), [Desktop app](https://github.com/leanote/desktop-app), [iOS](https://github.com/leanote/leanote-ios), [Android](https://github.com/leanote/leanote-android). And they are all open source!
* ...... * ......
@@ -50,7 +50,7 @@ More information about how to install Leanote please see:
* [Windows](https://github.com/leanote/leanote/wiki/leanote-source-installation-on-Windows-(En)) * [Windows](https://github.com/leanote/leanote/wiki/leanote-source-installation-on-Windows-(En))
* [Mac and Linux](https://github.com/leanote/leanote/wiki/leanote-binary-installation-on-Mac-and-Linux-(En)) * [Mac and Linux](https://github.com/leanote/leanote/wiki/leanote-binary-installation-on-Mac-and-Linux-(En))
* Leanote source installation tutorial: * Leanote source installation tutorial:
* [Windows](https://github.com/leanote/leanote/wiki/leanote-source-installation-on-Windows-(En)) <!-- * [Windows](https://github.com/leanote/leanote/wiki/leanote-source-installation-on-Windows-(En)) -->
* [Mac and Linux](https://github.com/leanote/leanote/wiki/Leanote-source-installation-on-Mac-and-Linux-(En)) * [Mac and Linux](https://github.com/leanote/leanote/wiki/Leanote-source-installation-on-Mac-and-Linux-(En))
## 4. Documentation ## 4. Documentation
@@ -66,7 +66,7 @@ If you are a developer yourself and feel like to build on top of Leanote, please
## 6. Contributions ## 6. Contributions
Like or dislike Leanote, please leave your comments and suggestions to help us improve it. Like or dislike Leanote, please leave your comments and suggestions to help us improve it.
If you encounter any issue, we suggest you first do a search in the issues section to see whether a solution already exists, or open up a new one otherwise. If you encounter any issue, we suggest you first search the issues section to see whether a solution already exists, or open up a new one otherwise.
Wed like to acknowledge the contributions made by our [developers and contributors](https://github.com/leanote/leanote/graphs/contributors) to Wed like to acknowledge the contributions made by our [developers and contributors](https://github.com/leanote/leanote/graphs/contributors) to
this project. Leanote wont exist without your hard work. Your help is much appreciated. this project. Leanote wont exist without your hard work. Your help is much appreciated.
@@ -81,12 +81,12 @@ If you find any problems or have any good ideas, feature requests, please submit
## 8. Donation ## 8. Donation
If you like our product, consider supporting us via [donate us](http://leanote.org/#donate). If you like our product, consider supporting us via [donate us](http://leanote.org/#donate).
We acknowledge the donations make by all the [donators](http://leanote.leanote.com/post/leanote-donation-list). We acknowledge the donations made by all the [donators](http://leanote.leanote.com/post/leanote-donation-list).
## 9. Related projects ## 9. Related projects
* [Leanote Desktop App](https://github.com/leanote/desktop-app), [Download](http://app.leanote.com) * [Leanote Desktop App](https://github.com/leanote/desktop-app), [Download](http://app.leanote.com)
* [Leanote iOS](https://github.com/leanote/leanote-ios), [Download From App Store](https://itunes.apple.com/en/app/leanote/id1022302858?mt=8) * [Leanote iOS](https://github.com/leanote/leanote-ios), [Download From App Store](https://itunes.apple.com/app/leanote/id1022302858)
* [Leanote Android](https://github.com/leanote/leanote-android), development phase * [Leanote Android](https://github.com/leanote/leanote-android), development phase
You are welcome to join us. You are welcome to join us.
@@ -145,7 +145,7 @@ Leanote云笔记产品包括: Leanote Web & Server(即本仓库), 桌面客户
* [Windows](https://github.com/leanote/leanote/wiki/Leanote%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%89%88%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B---Windows) * [Windows](https://github.com/leanote/leanote/wiki/Leanote%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%89%88%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B---Windows)
* [Mac, Linux](https://github.com/leanote/leanote/wiki/leanote%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B) * [Mac, Linux](https://github.com/leanote/leanote/wiki/leanote%E4%BA%8C%E8%BF%9B%E5%88%B6%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
* Leanote源码详细安装教程: * Leanote源码详细安装教程:
* [Windows](https://github.com/leanote/leanote/wiki/Leanote-%E6%BA%90%E7%A0%81%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B----Windows) <!-- * [Windows](https://github.com/leanote/leanote/wiki/Leanote-%E6%BA%90%E7%A0%81%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B----Windows) -->
* [Mac, Linux](https://github.com/leanote/leanote/wiki/leanote%E5%BC%80%E5%8F%91%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B) * [Mac, Linux](https://github.com/leanote/leanote/wiki/leanote%E5%BC%80%E5%8F%91%E7%89%88%E8%AF%A6%E7%BB%86%E5%AE%89%E8%A3%85%E6%95%99%E7%A8%8B)
## 4. 相关文档 ## 4. 相关文档
@@ -186,4 +186,3 @@ Leanote还有很多问题, 如果你喜欢它, 欢迎加入我们一起完善Lea
* [Leanote 社区](http://bbs.leanote.com) * [Leanote 社区](http://bbs.leanote.com)
* [QQ群](http://leanote.leanote.com/post/Leanote-groups) * [QQ群](http://leanote.leanote.com/post/Leanote-groups)
* [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote) * [Leanote Google Group](https://groups.google.com/forum/#!forum/leanote)

9
app/cmd/README.md Normal file
View File

@@ -0,0 +1,9 @@
全部代码来自https://github.com/revel/cmd
因为要改parse2, 所以改只要一点点代码
harness/
build.go 只要gensource, 其它的先return
main.go 改动很小
build.go 改动很小
parser2/
source_processors.go 改了 fsWalk 过滤掉 public, files, build 等文件夹

270
app/cmd/build.go Normal file
View File

@@ -0,0 +1,270 @@
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package main
import (
"os"
"path/filepath"
"strings"
"fmt"
"github.com/leanote/leanote/app/cmd/harness" // 只改了这个
"github.com/revel/cmd/model"
"github.com/revel/cmd/utils"
)
var cmdBuild = &Command{
UsageLine: "revel build [-r [run mode]] [import path] [target path] ",
Short: "build a Revel application (e.g. for deployment)",
Long: `
Build the Revel web application named by the given import path.
This allows it to be deployed and run on a machine that lacks a Go installation.
For example:
revel build github.com/revel/examples/chat /tmp/chat
`,
}
func init() {
cmdBuild.RunWith = buildApp
cmdBuild.UpdateConfig = updateBuildConfig
}
// The update config updates the configuration command so that it can run
func updateBuildConfig(c *model.CommandConfig, args []string) bool {
c.Index = model.BUILD
if c.Build.TargetPath == "" {
c.Build.TargetPath = "target"
}
if len(args) == 0 && c.Build.ImportPath != "" {
return true
}
// If arguments were passed in then there must be two
if len(args) < 2 {
fmt.Fprintf(os.Stderr, "%s\n%s", cmdBuild.UsageLine, cmdBuild.Long)
return false
}
c.Build.ImportPath = args[0]
c.Build.TargetPath = args[1]
if len(args) > 2 {
c.Build.Mode = args[2]
}
return true
}
// The main entry point to build application from command line
func buildApp(c *model.CommandConfig) (err error) {
appImportPath, destPath, mode := c.ImportPath, c.Build.TargetPath, DefaultRunMode
if len(c.Build.Mode) > 0 {
mode = c.Build.Mode
}
// Convert target to absolute path
c.Build.TargetPath, _ = filepath.Abs(destPath)
c.Build.Mode = mode
c.Build.ImportPath = appImportPath
revel_paths, err := model.NewRevelPaths(mode, appImportPath, c.AppPath, model.NewWrappedRevelCallback(nil, c.PackageResolver))
if err != nil {
return
}
if err = buildSafetyCheck(destPath); err != nil {
return
}
// Ensure the application can be built, this generates the main file
app, err := harness.Build(c, revel_paths)
if err != nil {
return err
}
// Copy files
// Included are:
// - run scripts
// - binary
// - revel
// - app
return // 改了这里
packageFolders, err := buildCopyFiles(c, app, revel_paths)
if err != nil {
return
}
err = buildCopyModules(c, revel_paths, packageFolders, app)
if err != nil {
return
}
err = buildWriteScripts(c, app)
if err != nil {
return
}
return
}
// Copy the files to the target
func buildCopyFiles(c *model.CommandConfig, app *harness.App, revel_paths *model.RevelContainer) (packageFolders []string, err error) {
appImportPath, destPath := c.ImportPath, c.Build.TargetPath
// Revel and the app are in a directory structure mirroring import path
srcPath := filepath.Join(destPath, "src")
destBinaryPath := filepath.Join(destPath, filepath.Base(app.BinaryPath))
tmpRevelPath := filepath.Join(srcPath, filepath.FromSlash(model.RevelImportPath))
if err = utils.CopyFile(destBinaryPath, filepath.Join(revel_paths.BasePath, app.BinaryPath)); err != nil {
return
}
utils.MustChmod(destBinaryPath, 0755)
// Copy the templates from the revel
if err = utils.CopyDir(filepath.Join(tmpRevelPath, "conf"), filepath.Join(revel_paths.RevelPath, "conf"), nil); err != nil {
return
}
if err = utils.CopyDir(filepath.Join(tmpRevelPath, "templates"), filepath.Join(revel_paths.RevelPath, "templates"), nil); err != nil {
return
}
// Get the folders to be packaged
packageFolders = strings.Split(revel_paths.Config.StringDefault("package.folders", "conf,public,app/views"), ",")
for i, p := range packageFolders {
// Clean spaces, reformat slash to filesystem
packageFolders[i] = filepath.FromSlash(strings.TrimSpace(p))
}
if c.Build.CopySource {
err = utils.CopyDir(filepath.Join(srcPath, filepath.FromSlash(appImportPath)), revel_paths.BasePath, nil)
if err != nil {
return
}
} else {
for _, folder := range packageFolders {
err = utils.CopyDir(
filepath.Join(srcPath, filepath.FromSlash(appImportPath), folder),
filepath.Join(revel_paths.BasePath, folder),
nil)
if err != nil {
return
}
}
}
return
}
// Based on the section copy over the build modules
func buildCopyModules(c *model.CommandConfig, revel_paths *model.RevelContainer, packageFolders []string, app *harness.App) (err error) {
destPath := filepath.Join(c.Build.TargetPath, "src")
// Find all the modules used and copy them over.
config := revel_paths.Config.Raw()
// We should only copy over the section of options what the build is targeted for
// We will default to prod
moduleImportList := []string{}
for _, section := range config.Sections() {
// If the runmode is defined we will only import modules defined for that run mode
if c.Build.Mode != "" && c.Build.Mode != section {
continue
}
options, _ := config.SectionOptions(section)
for _, key := range options {
if !strings.HasPrefix(key, "module.") {
continue
}
moduleImportPath, _ := config.String(section, key)
if moduleImportPath == "" {
continue
}
moduleImportList = append(moduleImportList, moduleImportPath)
}
}
// Copy the the paths for each of the modules
for _, importPath := range moduleImportList {
fsPath := app.PackagePathMap[importPath]
utils.Logger.Info("Copy files ", "to", filepath.Join(destPath, importPath), "from", fsPath)
if c.Build.CopySource {
err = utils.CopyDir(filepath.Join(destPath, importPath), fsPath, nil)
if err != nil {
return
}
} else {
for _, folder := range packageFolders {
err = utils.CopyDir(
filepath.Join(destPath, importPath, folder),
filepath.Join(fsPath, folder),
nil)
if err != nil {
return
}
}
}
}
return
}
// Write the run scripts for the build
func buildWriteScripts(c *model.CommandConfig, app *harness.App) (err error) {
tmplData := map[string]interface{}{
"BinName": filepath.Base(app.BinaryPath),
"ImportPath": c.Build.ImportPath,
"Mode": c.Build.Mode,
}
err = utils.GenerateTemplate(
filepath.Join(c.Build.TargetPath, "run.sh"),
PACKAGE_RUN_SH,
tmplData,
)
if err != nil {
return
}
utils.MustChmod(filepath.Join(c.Build.TargetPath, "run.sh"), 0755)
err = utils.GenerateTemplate(
filepath.Join(c.Build.TargetPath, "run.bat"),
PACKAGE_RUN_BAT,
tmplData,
)
if err != nil {
return
}
fmt.Println("Your application has been built in:", c.Build.TargetPath)
return
}
// Checks to see if the target folder exists and can be created
func buildSafetyCheck(destPath string) error {
// First, verify that it is either already empty or looks like a previous
// build (to avoid clobbering anything)
if utils.Exists(destPath) && !utils.Empty(destPath) && !utils.Exists(filepath.Join(destPath, "run.sh")) {
return utils.NewBuildError("Abort: %s exists and does not look like a build directory.", "path", destPath)
}
if err := os.RemoveAll(destPath); err != nil && !os.IsNotExist(err) {
return utils.NewBuildIfError(err, "Remove all error", "path", destPath)
}
if err := os.MkdirAll(destPath, 0777); err != nil {
return utils.NewBuildIfError(err, "MkDir all error", "path", destPath)
}
return nil
}
const PACKAGE_RUN_SH = `#!/bin/sh
SCRIPTPATH=$(cd "$(dirname "$0")"; pwd)
"$SCRIPTPATH/{{.BinName}}" -importPath {{.ImportPath}} -srcPath "$SCRIPTPATH/src" -runMode {{.Mode}}
`
const PACKAGE_RUN_BAT = `@echo off
{{.BinName}} -importPath {{.ImportPath}} -srcPath "%CD%\src" -runMode {{.Mode}}
`

4
app/cmd/gen_tmp.sh Normal file
View File

@@ -0,0 +1,4 @@
SCRIPTPATH=$(cd "$(dirname "$0")"; pwd)
cd $SCRIPTPATH
go run . build -v ../../ ./tmptmp
rm -rf ./tmptmp

219
app/cmd/harness/app.go Normal file
View File

@@ -0,0 +1,219 @@
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package harness
import (
"bytes"
"errors"
"fmt"
"io"
"os"
"os/exec"
"time"
"sync"
"github.com/revel/cmd/model"
"github.com/revel/cmd/utils"
"runtime"
)
// App contains the configuration for running a Revel app. (Not for the app itself)
// Its only purpose is constructing the command to execute.
type App struct {
BinaryPath string // Path to the app executable
Port int // Port to pass as a command line argument.
cmd AppCmd // The last cmd returned.
PackagePathMap map[string]string // Package to directory path map
Paths *model.RevelContainer
}
// NewApp returns app instance with binary path in it
func NewApp(binPath string, paths *model.RevelContainer, packagePathMap map[string]string) *App {
return &App{BinaryPath: binPath, Paths: paths, Port: paths.HTTPPort, PackagePathMap:packagePathMap}
}
// Cmd returns a command to run the app server using the current configuration.
func (a *App) Cmd(runMode string) AppCmd {
a.cmd = NewAppCmd(a.BinaryPath, a.Port, runMode, a.Paths)
return a.cmd
}
// Kill the last app command returned.
func (a *App) Kill() {
a.cmd.Kill()
}
// AppCmd manages the running of a Revel app server.
// It requires revel.Init to have been called previously.
type AppCmd struct {
*exec.Cmd
}
// NewAppCmd returns the AppCmd with parameters initialized for running app
func NewAppCmd(binPath string, port int, runMode string, paths *model.RevelContainer) AppCmd {
cmd := exec.Command(binPath,
fmt.Sprintf("-port=%d", port),
fmt.Sprintf("-importPath=%s", paths.ImportPath),
fmt.Sprintf("-runMode=%s", runMode))
cmd.Stdout, cmd.Stderr = os.Stdout, os.Stderr
return AppCmd{cmd}
}
// Start the app server, and wait until it is ready to serve requests.
func (cmd AppCmd) Start(c *model.CommandConfig) error {
listeningWriter := &startupListeningWriter{os.Stdout, make(chan bool), c, &bytes.Buffer{}}
cmd.Stdout = listeningWriter
utils.CmdInit(cmd.Cmd, !c.Vendored, c.AppPath)
utils.Logger.Info("Exec app:", "path", cmd.Path, "args", cmd.Args, "dir", cmd.Dir, "env", cmd.Env)
if err := cmd.Cmd.Start(); err != nil {
utils.Logger.Fatal("Error running:", "error", err)
}
select {
case exitState := <-cmd.waitChan():
fmt.Println("Startup failure view previous messages, \n Proxy is listening :", c.Run.Port)
err := utils.NewError("", "Revel Run Error", "starting your application there was an exception. See terminal output, " + exitState, "")
// TODO pretiffy command line output
// err.MetaError = listeningWriter.getLastOutput()
return err
case <-time.After(60 * time.Second):
println("Revel proxy is listening, point your browser to :", c.Run.Port)
utils.Logger.Error("Killing revel server process did not respond after wait timeout.", "processid", cmd.Process.Pid)
cmd.Kill()
return errors.New("revel/harness: app timed out")
case <-listeningWriter.notifyReady:
println("Revel proxy is listening, point your browser to :", c.Run.Port)
return nil
}
}
// Run the app server inline. Never returns.
func (cmd AppCmd) Run(c *model.CommandConfig) {
utils.CmdInit(cmd.Cmd, !c.Vendored, c.AppPath)
utils.Logger.Info("Exec app:", "path", cmd.Path, "args", cmd.Args)
if err := cmd.Cmd.Run(); err != nil {
utils.Logger.Fatal("Error running:", "error", err)
}
}
// Kill terminates the app server if it's running.
func (cmd AppCmd) Kill() {
if cmd.Cmd != nil && (cmd.ProcessState == nil || !cmd.ProcessState.Exited()) {
// Windows appears to send the kill to all threads, shutting down the
// server before this can, this check will ensure the process is still running
if _, err := os.FindProcess(int(cmd.Process.Pid)); err != nil {
// Server has already exited
utils.Logger.Info("Server not running revel server pid", "pid", cmd.Process.Pid)
return
}
// Wait for the shutdown channel
waitMutex := &sync.WaitGroup{}
waitMutex.Add(1)
ch := make(chan bool, 1)
go func() {
waitMutex.Done()
s, err := cmd.Process.Wait()
defer func() {
ch <- true
}()
if err != nil {
utils.Logger.Info("Wait failed for process ", "error", err)
}
if s != nil {
utils.Logger.Info("Revel App exited", "state", s.String())
}
}()
// Wait for the channel to begin waiting
waitMutex.Wait()
// Send an interrupt signal to allow for a graceful shutdown
utils.Logger.Info("Killing revel server pid", "pid", cmd.Process.Pid)
var err error
if runtime.GOOS != "windows" {
// os.Interrupt is not available on windows
err = cmd.Process.Signal(os.Interrupt)
}
if err != nil {
utils.Logger.Info(
"Revel app already exited.",
"processid", cmd.Process.Pid, "error", err,
"killerror", cmd.Process.Kill())
return
}
// Use a timer to ensure that the process exits
utils.Logger.Info("Waiting to exit")
select {
case <-ch:
return
case <-time.After(60 * time.Second):
// Kill the process
utils.Logger.Error(
"Revel app failed to exit in 60 seconds - killing.",
"processid", cmd.Process.Pid,
"killerror", cmd.Process.Kill())
}
utils.Logger.Info("Done Waiting to exit")
}
}
// Return a channel that is notified when Wait() returns.
func (cmd AppCmd) waitChan() <-chan string {
ch := make(chan string, 1)
go func() {
_ = cmd.Wait()
state := cmd.ProcessState
exitStatus := " unknown "
if state != nil {
exitStatus = state.String()
}
ch <- exitStatus
}()
return ch
}
// A io.Writer that copies to the destination, and listens for "Revel engine is listening on.."
// in the stream. (Which tells us when the revel server has finished starting up)
// This is super ghetto, but by far the simplest thing that should work.
type startupListeningWriter struct {
dest io.Writer
notifyReady chan bool
c *model.CommandConfig
buffer *bytes.Buffer
}
// Writes to this output stream
func (w *startupListeningWriter) Write(p []byte) (int, error) {
if w.notifyReady != nil && bytes.Contains(p, []byte("Revel engine is listening on")) {
w.notifyReady <- true
w.notifyReady = nil
}
if w.c.HistoricMode {
if w.notifyReady != nil && bytes.Contains(p, []byte("Listening on")) {
w.notifyReady <- true
w.notifyReady = nil
}
}
if w.notifyReady != nil {
w.buffer.Write(p)
}
return w.dest.Write(p)
}
// Returns the cleaned output from the response
// TODO clean the response more
func (w *startupListeningWriter) getLastOutput() string {
return w.buffer.String()
}

566
app/cmd/harness/build.go Normal file
View File

@@ -0,0 +1,566 @@
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
package harness
import (
"fmt"
"go/build"
"os"
"os/exec"
"path"
"path/filepath"
"regexp"
"runtime"
"sort"
"strconv"
"strings"
"time"
"github.com/leanote/leanote/app/cmd/parser2"
"github.com/revel/cmd/model"
"github.com/revel/cmd/parser"
_ "github.com/revel/cmd/parser"
"github.com/revel/cmd/utils"
)
var importErrorPattern = regexp.MustCompile("cannot find package \"([^\"]+)\"")
type ByString []*model.TypeInfo
func (c ByString) Len() int {
return len(c)
}
func (c ByString) Swap(i, j int) {
c[i], c[j] = c[j], c[i]
}
func (c ByString) Less(i, j int) bool {
return c[i].String() < c[j].String()
}
// Build the app:
// 1. Generate the the main.go file.
// 2. Run the appropriate "go build" command.
// Requires that revel.Init has been called previously.
// Returns the path to the built binary, and an error if there was a problem building it.
func Build(c *model.CommandConfig, paths *model.RevelContainer) (_ *App, err error) {
// First, clear the generated files (to avoid them messing with ProcessSource).
cleanSource(paths, "tmp", "routes")
var sourceInfo *model.SourceInfo
if c.HistoricBuildMode {
sourceInfo, err = parser.ProcessSource(paths)
} else {
sourceInfo, err = parser2.ProcessSource(paths)
}
if err != nil {
return
}
// Add the db.import to the import paths.
if dbImportPath, found := paths.Config.String("db.import"); found {
sourceInfo.InitImportPaths = append(sourceInfo.InitImportPaths, strings.Split(dbImportPath, ",")...)
}
// Sort controllers so that file generation is reproducible
controllers := sourceInfo.ControllerSpecs()
sort.Stable(ByString(controllers))
// Generate two source files.
templateArgs := map[string]interface{}{
"ImportPath": paths.ImportPath,
"Controllers": controllers,
"ValidationKeys": sourceInfo.ValidationKeys,
"ImportPaths": calcImportAliases(sourceInfo),
"TestSuites": sourceInfo.TestSuites(),
}
// Generate code for the main, run and routes file.
// The run file allows external programs to launch and run the application
// without being the main thread
cleanSource(paths, "tmp", "routes")
if err = genSource(paths, "tmp", "main.go", RevelMainTemplate, templateArgs); err != nil {
return
}
if err = genSource(paths, filepath.Join("tmp", "run"), "run.go", RevelRunTemplate, templateArgs); err != nil {
return
}
if err = genSource(paths, "routes", "routes.go", RevelRoutesTemplate, templateArgs); err != nil {
return
}
utils.Logger.Warn("gen tmp/main.go, tmp/run/run.go, routes/routes.go success!!")
return // 改了这里
// Read build config.
buildTags := paths.Config.StringDefault("build.tags", "")
// Build the user program (all code under app).
// It relies on the user having "go" installed.
goPath, err := exec.LookPath("go")
if err != nil {
utils.Logger.Fatal("Go executable not found in PATH.")
}
// Binary path is a combination of target/app directory, app's import path and its name.
binName := filepath.Join("target", "app", paths.ImportPath, filepath.Base(paths.BasePath))
// Change binary path for Windows build
goos := runtime.GOOS
if goosEnv := os.Getenv("GOOS"); goosEnv != "" {
goos = goosEnv
}
if goos == "windows" {
binName += ".exe"
}
gotten := make(map[string]struct{})
contains := func(s []string, e string) bool {
for _, a := range s {
if a == e {
return true
}
}
return false
}
if len(c.GoModFlags) > 0 {
for _, gomod := range c.GoModFlags {
goModCmd := exec.Command(goPath, append([]string{"mod"}, strings.Split(gomod, " ")...)...)
utils.CmdInit(goModCmd, !c.Vendored, c.AppPath)
output, err := goModCmd.CombinedOutput()
utils.Logger.Info("Gomod applied ", "output", string(output))
// If the build succeeded, we're done.
if err != nil {
utils.Logger.Error("Gomod Failed continuing ", "error", err, "output", string(output))
}
}
}
for {
appVersion := getAppVersion(paths)
if appVersion == "" {
appVersion = "noVersionProvided"
}
buildTime := time.Now().UTC().Format(time.RFC3339)
versionLinkerFlags := fmt.Sprintf("-X '%s/app.AppVersion=%s' -X '%s/app.BuildTime=%s'",
paths.ImportPath, appVersion, paths.ImportPath, buildTime)
// Append any build flags specified, they will override existing flags
flags := []string{}
if len(c.BuildFlags) == 0 {
flags = []string{
"build",
"-ldflags", versionLinkerFlags,
"-tags", buildTags,
"-o", binName}
} else {
if !contains(c.BuildFlags, "build") {
flags = []string{"build"}
}
if !contains(flags, "-ldflags") {
ldflags := "-ldflags= " + versionLinkerFlags
// Add user defined build flags
for i := range c.BuildFlags {
ldflags += " -X '" + c.BuildFlags[i] + "'"
}
flags = append(flags, ldflags)
}
if !contains(flags, "-tags") && buildTags != "" {
flags = append(flags, "-tags", buildTags)
}
if !contains(flags, "-o") {
flags = append(flags, "-o", binName)
}
}
// Note: It's not applicable for filepath.* usage
flags = append(flags, path.Join(paths.ImportPath, "app", "tmp"))
buildCmd := exec.Command(goPath, flags...)
if !c.Vendored {
// This is Go main path
gopath := c.GoPath
for _, o := range paths.ModulePathMap {
gopath += string(filepath.ListSeparator) + o.Path
}
buildCmd.Env = append(os.Environ(),
"GOPATH=" + gopath,
)
}
utils.CmdInit(buildCmd, !c.Vendored, c.AppPath)
utils.Logger.Info("Exec:", "args", buildCmd.Args, "working dir", buildCmd.Dir)
output, err := buildCmd.CombinedOutput()
// If the build succeeded, we're done.
if err == nil {
utils.Logger.Info("Build successful continuing")
return NewApp(binName, paths, sourceInfo.PackageMap), nil
}
// Since there was an error, capture the output in case we need to report it
stOutput := string(output)
utils.Logger.Infof("Got error on build of app %s", stOutput)
// See if it was an import error that we can go get.
matches := importErrorPattern.FindAllStringSubmatch(stOutput, -1)
utils.Logger.Info("Build failed checking for missing imports", "message", stOutput, "missing_imports", len(matches))
if matches == nil {
utils.Logger.Info("Build failed no missing imports", "message", stOutput)
return nil, newCompileError(paths, output)
}
utils.Logger.Warn("Detected missing packages, importing them", "packages", len(matches))
for _, match := range matches {
// Ensure we haven't already tried to go get it.
pkgName := match[1]
utils.Logger.Info("Trying to import ", "package", pkgName)
if _, alreadyTried := gotten[pkgName]; alreadyTried {
utils.Logger.Error("Failed to import ", "package", pkgName)
return nil, newCompileError(paths, output)
}
gotten[pkgName] = struct{}{}
if err := c.PackageResolver(pkgName); err != nil {
utils.Logger.Error("Unable to resolve package", "package", pkgName, "error", err)
return nil, newCompileError(paths, []byte(err.Error()))
}
}
// Success getting the import, attempt to build again.
}
// TODO remove this unreachable code and document it
utils.Logger.Fatal("Not reachable")
return nil, nil
}
// Try to define a version string for the compiled app
// The following is tried (first match returns):
// - Read a version explicitly specified in the APP_VERSION environment
// variable
// - Read the output of "git describe" if the source is in a git repository
// If no version can be determined, an empty string is returned.
func getAppVersion(paths *model.RevelContainer) string {
if version := os.Getenv("APP_VERSION"); version != "" {
return version
}
// Check for the git binary
if gitPath, err := exec.LookPath("git"); err == nil {
// Check for the .git directory
gitDir := filepath.Join(paths.BasePath, ".git")
info, err := os.Stat(gitDir)
if (err != nil && os.IsNotExist(err)) || !info.IsDir() {
return ""
}
gitCmd := exec.Command(gitPath, "--git-dir=" + gitDir, "--work-tree=" + paths.BasePath, "describe", "--always", "--dirty")
utils.Logger.Info("Exec:", "args", gitCmd.Args)
output, err := gitCmd.Output()
if err != nil {
utils.Logger.Error("Cannot determine git repository version:", "error", err)
return ""
}
return "git-" + strings.TrimSpace(string(output))
}
return ""
}
func cleanSource(paths *model.RevelContainer, dirs ...string) {
for _, dir := range dirs {
cleanDir(paths, dir)
}
}
func cleanDir(paths *model.RevelContainer, dir string) {
utils.Logger.Info("Cleaning dir ", "dir", dir)
tmpPath := filepath.Join(paths.AppPath, dir)
f, err := os.Open(tmpPath)
if err != nil {
if !os.IsNotExist(err) {
utils.Logger.Error("Failed to clean dir:", "error", err)
}
} else {
defer func() {
_ = f.Close()
}()
infos, err := f.Readdir(0)
if err != nil {
if !os.IsNotExist(err) {
utils.Logger.Fatal("Failed to clean dir:", "error", err)
}
} else {
for _, info := range infos {
pathName := filepath.Join(tmpPath, info.Name())
if info.IsDir() {
err := os.RemoveAll(pathName)
if err != nil {
utils.Logger.Fatal("Failed to remove dir:", "error", err)
}
} else {
err := os.Remove(pathName)
if err != nil {
utils.Logger.Fatal("Failed to remove file:", "error", err)
}
}
}
}
}
}
// genSource renders the given template to produce source code, which it writes
// to the given directory and file.
func genSource(paths *model.RevelContainer, dir, filename, templateSource string, args map[string]interface{}) error {
return utils.GenerateTemplate(filepath.Join(paths.AppPath, dir, filename), templateSource, args)
}
// Looks through all the method args and returns a set of unique import paths
// that cover all the method arg types.
// Additionally, assign package aliases when necessary to resolve ambiguity.
func calcImportAliases(src *model.SourceInfo) map[string]string {
aliases := make(map[string]string)
typeArrays := [][]*model.TypeInfo{src.ControllerSpecs(), src.TestSuites()}
for _, specs := range typeArrays {
for _, spec := range specs {
addAlias(aliases, spec.ImportPath, spec.PackageName)
for _, methSpec := range spec.MethodSpecs {
for _, methArg := range methSpec.Args {
if methArg.ImportPath == "" {
continue
}
addAlias(aliases, methArg.ImportPath, methArg.TypeExpr.PkgName)
}
}
}
}
// Add the "InitImportPaths", with alias "_"
for _, importPath := range src.InitImportPaths {
if _, ok := aliases[importPath]; !ok {
aliases[importPath] = "_"
}
}
return aliases
}
// Adds an alias to the map of alias names
func addAlias(aliases map[string]string, importPath, pkgName string) {
alias, ok := aliases[importPath]
if ok {
return
}
alias = makePackageAlias(aliases, pkgName)
aliases[importPath] = alias
}
// Generates a package alias
func makePackageAlias(aliases map[string]string, pkgName string) string {
i := 0
alias := pkgName
for containsValue(aliases, alias) || alias == "revel" {
alias = fmt.Sprintf("%s%d", pkgName, i)
i++
}
return alias
}
// Returns true if this value is in the map
func containsValue(m map[string]string, val string) bool {
for _, v := range m {
if v == val {
return true
}
}
return false
}
// Parse the output of the "go build" command.
// Return a detailed Error.
func newCompileError(paths *model.RevelContainer, output []byte) *utils.SourceError {
errorMatch := regexp.MustCompile(`(?m)^([^:#]+):(\d+):(\d+:)? (.*)$`).
FindSubmatch(output)
if errorMatch == nil {
errorMatch = regexp.MustCompile(`(?m)^(.*?):(\d+):\s(.*?)$`).FindSubmatch(output)
if errorMatch == nil {
utils.Logger.Error("Failed to parse build errors", "error", string(output))
return &utils.SourceError{
SourceType: "Go code",
Title: "Go Compilation Error",
Description: "See console for build error.",
}
}
errorMatch = append(errorMatch, errorMatch[3])
utils.Logger.Error("Build errors", "errors", string(output))
}
findInPaths := func(relFilename string) string {
// Extract the paths from the gopaths, and search for file there first
gopaths := filepath.SplitList(build.Default.GOPATH)
for _, gp := range gopaths {
newPath := filepath.Join(gp, "src", paths.ImportPath, relFilename)
println(newPath)
if utils.Exists(newPath) {
return newPath
}
}
newPath, _ := filepath.Abs(relFilename)
utils.Logger.Warn("Could not find in GO path", "file", relFilename)
return newPath
}
// Read the source for the offending file.
var (
relFilename = string(errorMatch[1]) // e.g. "src/revel/sample/app/controllers/app.go"
absFilename = findInPaths(relFilename)
line, _ = strconv.Atoi(string(errorMatch[2]))
description = string(errorMatch[4])
compileError = &utils.SourceError{
SourceType: "Go code",
Title: "Go Compilation Error",
Path: relFilename,
Description: description,
Line: line,
}
)
errorLink := paths.Config.StringDefault("error.link", "")
if errorLink != "" {
compileError.SetLink(errorLink)
}
fileStr, err := utils.ReadLines(absFilename)
if err != nil {
compileError.MetaError = absFilename + ": " + err.Error()
utils.Logger.Info("Unable to readlines " + compileError.MetaError, "error", err)
return compileError
}
compileError.SourceLines = fileStr
return compileError
}
// RevelMainTemplate template for app/tmp/run/run.go
const RevelRunTemplate = `// GENERATED CODE - DO NOT EDIT
// This file is the run file for Revel.
// It registers all the controllers and provides details for the Revel server engine to
// properly inject parameters directly into the action endpoints.
package run
import (
"reflect"
"github.com/revel/revel"{{range $k, $v := $.ImportPaths}}
{{$v}} "{{$k}}"{{end}}
"github.com/revel/revel/testing"
)
var (
// So compiler won't complain if the generated code doesn't reference reflect package...
_ = reflect.Invalid
)
// Register and run the application
func Run(port int) {
Register()
revel.Run(port)
}
// Register all the controllers
func Register() {
revel.AppLog.Info("Running revel server")
{{range $i, $c := .Controllers}}
revel.RegisterController((*{{index $.ImportPaths .ImportPath}}.{{.StructName}})(nil),
[]*revel.MethodType{
{{range .MethodSpecs}}&revel.MethodType{
Name: "{{.Name}}",
Args: []*revel.MethodArg{ {{range .Args}}
&revel.MethodArg{Name: "{{.Name}}", Type: reflect.TypeOf((*{{index $.ImportPaths .ImportPath | .TypeExpr.TypeName}})(nil)) },{{end}}
},
RenderArgNames: map[int][]string{ {{range .RenderCalls}}
{{.Line}}: []string{ {{range .Names}}
"{{.}}",{{end}}
},{{end}}
},
},
{{end}}
})
{{end}}
revel.DefaultValidationKeys = map[string]map[int]string{ {{range $path, $lines := .ValidationKeys}}
"{{$path}}": { {{range $line, $key := $lines}}
{{$line}}: "{{$key}}",{{end}}
},{{end}}
}
testing.TestSuites = []interface{}{ {{range .TestSuites}}
(*{{index $.ImportPaths .ImportPath}}.{{.StructName}})(nil),{{end}}
}
}
`
const RevelMainTemplate = `// GENERATED CODE - DO NOT EDIT
// This file is the main file for Revel.
// It registers all the controllers and provides details for the Revel server engine to
// properly inject parameters directly into the action endpoints.
package main
import (
"flag"
"{{.ImportPath}}/app/tmp/run"
"github.com/revel/revel"
)
var (
runMode *string = flag.String("runMode", "", "Run mode.")
port *int = flag.Int("port", 0, "By default, read from app.conf")
importPath *string = flag.String("importPath", "", "Go Import Path for the app.")
srcPath *string = flag.String("srcPath", "", "Path to the source root.")
)
func main() {
flag.Parse()
revel.Init(*runMode, *importPath, *srcPath)
run.Run(*port)
}
`
// RevelRoutesTemplate template for app/conf/routes
const RevelRoutesTemplate = `// GENERATED CODE - DO NOT EDIT
// This file provides a way of creating URL's based on all the actions
// found in all the controllers.
package routes
import "github.com/revel/revel"
{{range $i, $c := .Controllers}}
type t{{.StructName}} struct {}
var {{.StructName}} t{{.StructName}}
{{range .MethodSpecs}}
func (_ t{{$c.StructName}}) {{.Name}}({{range .Args}}
{{.Name}} {{if .ImportPath}}interface{}{{else}}{{.TypeExpr.TypeName ""}}{{end}},{{end}}
) string {
args := make(map[string]string)
{{range .Args}}
revel.Unbind(args, "{{.Name}}", {{.Name}}){{end}}
return revel.MainRouter.Reverse("{{$c.StructName}}.{{.Name}}", args).URL
}
{{end}}
{{end}}
`

411
app/cmd/harness/harness.go Normal file
View File

@@ -0,0 +1,411 @@
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// Package harness for a Revel Framework.
//
// It has a following responsibilities:
// 1. Parse the user program, generating a main.go file that registers
// controller classes and starts the user's server.
// 2. Build and run the user program. Show compile errors.
// 3. Monitor the user source and re-build / restart the program when necessary.
//
// Source files are generated in the app/tmp directory.
package harness
import (
"crypto/tls"
"fmt"
"time"
"go/build"
"io"
"net"
"net/http"
"net/http/httputil"
"net/url"
"os"
"os/signal"
"path/filepath"
"strings"
"sync/atomic"
"github.com/revel/cmd/model"
"github.com/revel/cmd/utils"
"github.com/revel/cmd/watcher"
"html/template"
"io/ioutil"
"sync"
"encoding/json"
)
var (
doNotWatch = []string{"tmp", "views", "routes"}
lastRequestHadError int32
)
// Harness reverse proxies requests to the application server.
// It builds / runs / rebuilds / restarts the server when code is changed.
type Harness struct {
app *App // The application
useProxy bool // True if proxy is in use
serverHost string // The proxy server host
port int // The proxy serber port
proxy *httputil.ReverseProxy // The proxy
watcher *watcher.Watcher // The file watched
mutex *sync.Mutex // A mutex to prevent concurrent updates
paths *model.RevelContainer // The Revel container
config *model.CommandConfig // The configuration
runMode string // The runmode the harness is running in
isError bool // True if harness is in error state
ranOnce bool // True app compiled once
}
func (h *Harness) renderError(iw http.ResponseWriter, ir *http.Request, err error) {
// Render error here
// Grab the template from three places
// 1) Application/views/errors
// 2) revel_home/views/errors
// 3) views/errors
if err == nil {
utils.Logger.Panic("Caller passed in a nil error")
}
templateSet := template.New("__root__")
seekViewOnPath := func(view string) (path string) {
path = filepath.Join(h.paths.ViewsPath, "errors", view)
if !utils.Exists(path) {
path = filepath.Join(h.paths.RevelPath, "templates", "errors", view)
}
data, err := ioutil.ReadFile(path)
if err != nil {
utils.Logger.Error("Unable to read template file", path)
}
_, err = templateSet.New("errors/" + view).Parse(string(data))
if err != nil {
utils.Logger.Error("Unable to parse template file", path)
}
return
}
target := []string{seekViewOnPath("500.html"), seekViewOnPath("500-dev.html")}
if !utils.Exists(target[0]) {
fmt.Fprintf(iw, "Target template not found not found %s<br />\n", target[0])
fmt.Fprintf(iw, "An error ocurred %s", err.Error())
return
}
var revelError *utils.SourceError
switch e := err.(type) {
case *utils.SourceError:
revelError = e
case error:
revelError = &utils.SourceError{
Title: "Server Error",
Description: e.Error(),
}
}
if revelError == nil {
panic("no error provided")
}
viewArgs := map[string]interface{}{}
viewArgs["RunMode"] = h.paths.RunMode
viewArgs["DevMode"] = h.paths.DevMode
viewArgs["Error"] = revelError
// Render the template from the file
err = templateSet.ExecuteTemplate(iw, "errors/500.html", viewArgs)
if err != nil {
utils.Logger.Error("Failed to execute", "error", err)
}
}
// ServeHTTP handles all requests.
// It checks for changes to app, rebuilds if necessary, and forwards the request.
func (h *Harness) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Don't rebuild the app for favicon requests.
if lastRequestHadError > 0 && r.URL.Path == "/favicon.ico" {
return
}
// Flush any change events and rebuild app if necessary.
// Render an error page if the rebuild / restart failed.
err := h.watcher.Notify()
if err != nil {
// In a thread safe manner update the flag so that a request for
// /favicon.ico does not trigger a rebuild
atomic.CompareAndSwapInt32(&lastRequestHadError, 0, 1)
h.renderError(w, r, err)
return
}
// In a thread safe manner update the flag so that a request for
// /favicon.ico is allowed
atomic.CompareAndSwapInt32(&lastRequestHadError, 1, 0)
// Reverse proxy the request.
// (Need special code for websockets, courtesy of bradfitz)
if strings.EqualFold(r.Header.Get("Upgrade"), "websocket") {
h.proxyWebsocket(w, r, h.serverHost)
} else {
h.proxy.ServeHTTP(w, r)
}
}
// NewHarness method returns a reverse proxy that forwards requests
// to the given port.
func NewHarness(c *model.CommandConfig, paths *model.RevelContainer, runMode string, noProxy bool) *Harness {
// Get a template loader to render errors.
// Prefer the app's views/errors directory, and fall back to the stock error pages.
//revel.MainTemplateLoader = revel.NewTemplateLoader(
// []string{filepath.Join(revel.RevelPath, "templates")})
//if err := revel.MainTemplateLoader.Refresh(); err != nil {
// revel.RevelLog.Error("Template loader error", "error", err)
//}
addr := paths.HTTPAddr
port := paths.Config.IntDefault("harness.port", 0)
scheme := "http"
if paths.HTTPSsl {
scheme = "https"
}
// If the server is running on the wildcard address, use "localhost"
if addr == "" {
utils.Logger.Warn("No http.addr specified in the app.conf listening on localhost interface only. " +
"This will not allow external access to your application")
addr = "localhost"
}
if port == 0 {
port = getFreePort()
}
serverURL, _ := url.ParseRequestURI(fmt.Sprintf(scheme+"://%s:%d", addr, port))
serverHarness := &Harness{
port: port,
serverHost: serverURL.String()[len(scheme+"://"):],
proxy: httputil.NewSingleHostReverseProxy(serverURL),
mutex: &sync.Mutex{},
paths: paths,
useProxy: !noProxy,
config: c,
runMode: runMode,
}
if paths.HTTPSsl {
serverHarness.proxy.Transport = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
}
return serverHarness
}
// Refresh method rebuilds the Revel application and run it on the given port.
// called by the watcher
func (h *Harness) Refresh() (err *utils.SourceError) {
t := time.Now();
fmt.Println("Changed detected, recompiling")
err = h.refresh()
if err!=nil && !h.ranOnce && h.useProxy {
addr := fmt.Sprintf("%s:%d", h.paths.HTTPAddr, h.paths.HTTPPort)
fmt.Printf("\nError compiling code, to view error details see proxy running on http://%s\n\n",addr)
}
h.ranOnce = true
fmt.Printf("\nTime to recompile %s\n",time.Now().Sub(t).String())
return
}
func (h *Harness) refresh() (err *utils.SourceError) {
// Allow only one thread to rebuild the process
// If multiple requests to rebuild are queued only the last one is executed on
// So before a build is started we wait for a second to determine if
// more requests for a build are triggered.
// Once no more requests are triggered the build will be processed
h.mutex.Lock()
defer h.mutex.Unlock()
if h.app != nil {
h.app.Kill()
}
utils.Logger.Info("Rebuild Called")
var newErr error
h.app, newErr = Build(h.config, h.paths)
if newErr != nil {
utils.Logger.Error("Build detected an error", "error", newErr)
if castErr, ok := newErr.(*utils.SourceError); ok {
return castErr
}
err = &utils.SourceError{
Title: "App failed to start up",
Description: err.Error(),
}
return
}
if h.useProxy {
h.app.Port = h.port
runMode := h.runMode
if !h.config.HistoricMode {
// Recalulate run mode based on the config
var paths []byte
if len(h.app.PackagePathMap)>0 {
paths, _ = json.Marshal(h.app.PackagePathMap)
}
runMode = fmt.Sprintf(`{"mode":"%s", "specialUseFlag":%v,"packagePathMap":%s}`, h.app.Paths.RunMode, h.config.Verbose, string(paths))
}
if err2 := h.app.Cmd(runMode).Start(h.config); err2 != nil {
utils.Logger.Error("Could not start application", "error", err2)
if err,k :=err2.(*utils.SourceError);k {
return err
}
return &utils.SourceError{
Title: "App failed to start up",
Description: err2.Error(),
}
}
} else {
h.app = nil
}
return
}
// WatchDir method returns false to file matches with doNotWatch
// otheriwse true
func (h *Harness) WatchDir(info os.FileInfo) bool {
return !utils.ContainsString(doNotWatch, info.Name())
}
// WatchFile method returns true given filename HasSuffix of ".go"
// otheriwse false - implements revel.DiscerningListener
func (h *Harness) WatchFile(filename string) bool {
return strings.HasSuffix(filename, ".go")
}
// Run the harness, which listens for requests and proxies them to the app
// server, which it runs and rebuilds as necessary.
func (h *Harness) Run() {
var paths []string
if h.paths.Config.BoolDefault("watch.gopath", false) {
gopaths := filepath.SplitList(build.Default.GOPATH)
paths = append(paths, gopaths...)
}
paths = append(paths, h.paths.CodePaths...)
h.watcher = watcher.NewWatcher(h.paths, false)
h.watcher.Listen(h, paths...)
go h.Refresh()
// h.watcher.Notify()
if h.useProxy {
go func() {
// Check the port to start on a random port
if h.paths.HTTPPort == 0 {
h.paths.HTTPPort = getFreePort()
}
addr := fmt.Sprintf("%s:%d", h.paths.HTTPAddr, h.paths.HTTPPort)
utils.Logger.Infof("Proxy server is listening on %s", addr)
var err error
if h.paths.HTTPSsl {
err = http.ListenAndServeTLS(
addr,
h.paths.HTTPSslCert,
h.paths.HTTPSslKey,
h)
} else {
err = http.ListenAndServe(addr, h)
}
if err != nil {
utils.Logger.Error("Failed to start reverse proxy:", "error", err)
}
}()
}
// Make a new channel to listen for the interrupt event
ch := make(chan os.Signal)
signal.Notify(ch, os.Interrupt, os.Kill)
<-ch
// Kill the app and exit
if h.app != nil {
h.app.Kill()
}
os.Exit(1)
}
// Find an unused port
func getFreePort() (port int) {
conn, err := net.Listen("tcp", ":0")
if err != nil {
utils.Logger.Fatal("Unable to fetch a freee port address", "error", err)
}
port = conn.Addr().(*net.TCPAddr).Port
err = conn.Close()
if err != nil {
utils.Logger.Fatal("Unable to close port", "error", err)
}
return port
}
// proxyWebsocket copies data between websocket client and server until one side
// closes the connection. (ReverseProxy doesn't work with websocket requests.)
func (h *Harness) proxyWebsocket(w http.ResponseWriter, r *http.Request, host string) {
var (
d net.Conn
err error
)
if h.paths.HTTPSsl {
// since this proxy isn't used in production,
// it's OK to set InsecureSkipVerify to true
// no need to add another configuration option.
d, err = tls.Dial("tcp", host, &tls.Config{InsecureSkipVerify: true})
} else {
d, err = net.Dial("tcp", host)
}
if err != nil {
http.Error(w, "Error contacting backend server.", 500)
utils.Logger.Error("Error dialing websocket backend ", "host", host, "error", err)
return
}
hj, ok := w.(http.Hijacker)
if !ok {
http.Error(w, "Not a hijacker?", 500)
return
}
nc, _, err := hj.Hijack()
if err != nil {
utils.Logger.Error("Hijack error", "error", err)
return
}
defer func() {
if err = nc.Close(); err != nil {
utils.Logger.Error("Connection close error", "error", err)
}
if err = d.Close(); err != nil {
utils.Logger.Error("Dial close error", "error", err)
}
}()
err = r.Write(d)
if err != nil {
utils.Logger.Error("Error copying request to target", "error", err)
return
}
errc := make(chan error, 2)
cp := func(dst io.Writer, src io.Reader) {
_, err := io.Copy(dst, src)
errc <- err
}
go cp(d, nc)
go cp(nc, d)
<-errc
}

View File

@@ -1,19 +0,0 @@
package main
import (
"fmt"
"github.com/revel/cmd/harness"
"github.com/revel/revel"
)
func main() {
// go run main.go
// 生成routes.go, main.go
revel.Init("", "github.com/leanote/leanote", "")
_, err := harness.Build() // ok, err
if err != nil {
panic(err)
}
fmt.Println("Ok")
// panicOnError(reverr, "Failed to build")
}

View File

@@ -0,0 +1,427 @@
package parser2
import (
"github.com/revel/cmd/utils"
"golang.org/x/tools/go/packages"
"github.com/revel/cmd/model"
"go/ast"
"go/token"
"strings"
"path/filepath"
"github.com/revel/cmd/logger"
)
type (
SourceInfoProcessor struct {
sourceProcessor *SourceProcessor
}
)
func NewSourceInfoProcessor(sourceProcessor *SourceProcessor) *SourceInfoProcessor {
return &SourceInfoProcessor{sourceProcessor:sourceProcessor}
}
func (s *SourceInfoProcessor) processPackage(p *packages.Package) (sourceInfo *model.SourceInfo) {
sourceInfo = &model.SourceInfo{
ValidationKeys: map[string]map[int]string{},
}
var (
isController = strings.HasSuffix(p.PkgPath, "/controllers") ||
strings.Contains(p.PkgPath, "/controllers/")
isTest = strings.HasSuffix(p.PkgPath, "/tests") ||
strings.Contains(p.PkgPath, "/tests/")
methodMap = map[string][]*model.MethodSpec{}
)
localImportMap := map[string]string{}
log := s.sourceProcessor.log.New("package", p.PkgPath)
log.Info("Processing package")
for _, tree := range p.Syntax {
for _, decl := range tree.Decls {
s.sourceProcessor.packageMap[p.PkgPath] = filepath.Dir(p.Fset.Position(decl.Pos()).Filename)
if !s.addImport(decl, p, localImportMap, log) {
continue
}
spec, found := s.getStructTypeDecl(decl, p.Fset)
//log.Info("Checking file","filename", p.Fset.Position(decl.Pos()).Filename,"found",found)
if found {
if isController || isTest {
controllerSpec := s.getControllerSpec(spec, p, localImportMap)
sourceInfo.StructSpecs = append(sourceInfo.StructSpecs, controllerSpec)
}
} else {
// Not a type definition, this could be a method for a controller try to extract that
// Func declaration?
funcDecl, ok := decl.(*ast.FuncDecl)
if !ok {
continue
}
// This could be a controller action endpoint, check and add if needed
if isController &&
funcDecl.Recv != nil && // Must have a receiver
funcDecl.Name.IsExported() && // be public
funcDecl.Type.Results != nil && len(funcDecl.Type.Results.List) == 1 {
// return one result
if m, receiver := s.getControllerFunc(funcDecl, p, localImportMap); m != nil {
methodMap[receiver] = append(methodMap[receiver], m)
log.Info("Added method map to ", "receiver", receiver, "method", m.Name)
}
}
// Check for validation
if lineKeyMap := s.getValidation(funcDecl, p); len(lineKeyMap) > 1 {
sourceInfo.ValidationKeys[p.PkgPath + "." + s.getFuncName(funcDecl)] = lineKeyMap
}
if funcDecl.Name.Name == "init" {
sourceInfo.InitImportPaths = append(sourceInfo.InitImportPaths, p.PkgPath)
}
}
}
}
// Add the method specs to the struct specs.
for _, spec := range sourceInfo.StructSpecs {
spec.MethodSpecs = methodMap[spec.StructName]
}
return
}
// Scan app source code for calls to X.Y(), where X is of type *Validation.
//
// Recognize these scenarios:
// - "Y" = "Validation" and is a member of the receiver.
// (The common case for inline validation)
// - "X" is passed in to the func as a parameter.
// (For structs implementing Validated)
//
// The line number to which a validation call is attributed is that of the
// surrounding ExprStmt. This is so that it matches what runtime.Callers()
// reports.
//
// The end result is that we can set the default validation key for each call to
// be the same as the local variable.
func (s *SourceInfoProcessor) getValidation(funcDecl *ast.FuncDecl, p *packages.Package) (map[int]string) {
var (
lineKeys = make(map[int]string)
// Check the func parameters and the receiver's members for the *revel.Validation type.
validationParam = s.getValidationParameter(funcDecl)
)
ast.Inspect(funcDecl.Body, func(node ast.Node) bool {
// e.g. c.Validation.Required(arg) or v.Required(arg)
callExpr, ok := node.(*ast.CallExpr)
if !ok {
return true
}
// e.g. c.Validation.Required or v.Required
funcSelector, ok := callExpr.Fun.(*ast.SelectorExpr)
if !ok {
return true
}
switch x := funcSelector.X.(type) {
case *ast.SelectorExpr: // e.g. c.Validation
if x.Sel.Name != "Validation" {
return true
}
case *ast.Ident: // e.g. v
if validationParam == nil || x.Obj != validationParam {
return true
}
default:
return true
}
if len(callExpr.Args) == 0 {
return true
}
// Given the validation expression, extract the key.
key := callExpr.Args[0]
switch expr := key.(type) {
case *ast.BinaryExpr:
// If the argument is a binary expression, take the first expression.
// (e.g. c.Validation.Required(myName != ""))
key = expr.X
case *ast.UnaryExpr:
// If the argument is a unary expression, drill in.
// (e.g. c.Validation.Required(!myBool)
key = expr.X
case *ast.BasicLit:
// If it's a literal, skip it.
return true
}
if typeExpr := model.NewTypeExprFromAst("", key); typeExpr.Valid {
lineKeys[p.Fset.Position(callExpr.End()).Line] = typeExpr.TypeName("")
} else {
s.sourceProcessor.log.Error("Error: Failed to generate key for field validation. Make sure the field name is valid.", "file", p.PkgPath,
"line", p.Fset.Position(callExpr.End()).Line, "function", funcDecl.Name.String())
}
return true
})
return lineKeys
}
// Check to see if there is a *revel.Validation as an argument.
func (s *SourceInfoProcessor) getValidationParameter(funcDecl *ast.FuncDecl) *ast.Object {
for _, field := range funcDecl.Type.Params.List {
starExpr, ok := field.Type.(*ast.StarExpr) // e.g. *revel.Validation
if !ok {
continue
}
selExpr, ok := starExpr.X.(*ast.SelectorExpr) // e.g. revel.Validation
if !ok {
continue
}
xIdent, ok := selExpr.X.(*ast.Ident) // e.g. rev
if !ok {
continue
}
if selExpr.Sel.Name == "Validation" && s.sourceProcessor.importMap[xIdent.Name] == model.RevelImportPath {
return field.Names[0].Obj
}
}
return nil
}
func (s *SourceInfoProcessor) getControllerFunc(funcDecl *ast.FuncDecl, p *packages.Package, localImportMap map[string]string) (method *model.MethodSpec, recvTypeName string) {
selExpr, ok := funcDecl.Type.Results.List[0].Type.(*ast.SelectorExpr)
if !ok {
return
}
if selExpr.Sel.Name != "Result" {
return
}
if pkgIdent, ok := selExpr.X.(*ast.Ident); !ok || s.sourceProcessor.importMap[pkgIdent.Name] != model.RevelImportPath {
return
}
method = &model.MethodSpec{
Name: funcDecl.Name.Name,
}
// Add a description of the arguments to the method.
for _, field := range funcDecl.Type.Params.List {
for _, name := range field.Names {
var importPath string
typeExpr := model.NewTypeExprFromAst(p.Name, field.Type)
if !typeExpr.Valid {
utils.Logger.Warn("Warn: Didn't understand argument '%s' of action %s. Ignoring.", name, s.getFuncName(funcDecl))
return // We didn't understand one of the args. Ignore this action.
}
// Local object
if typeExpr.PkgName == p.Name {
importPath = p.PkgPath
} else if typeExpr.PkgName != "" {
var ok bool
if importPath, ok = localImportMap[typeExpr.PkgName]; !ok {
if importPath, ok = s.sourceProcessor.importMap[typeExpr.PkgName]; !ok {
utils.Logger.Error("Unable to find import", "importMap", s.sourceProcessor.importMap, "localimport", localImportMap)
utils.Logger.Fatalf("Failed to find import for arg of type: %s , %s", typeExpr.PkgName, typeExpr.TypeName(""))
}
}
}
method.Args = append(method.Args, &model.MethodArg{
Name: name.Name,
TypeExpr: typeExpr,
ImportPath: importPath,
})
}
}
// Add a description of the calls to Render from the method.
// Inspect every node (e.g. always return true).
method.RenderCalls = []*model.MethodCall{}
ast.Inspect(funcDecl.Body, func(node ast.Node) bool {
// Is it a function call?
callExpr, ok := node.(*ast.CallExpr)
if !ok {
return true
}
// Is it calling (*Controller).Render?
selExpr, ok := callExpr.Fun.(*ast.SelectorExpr)
if !ok {
return true
}
// The type of the receiver is not easily available, so just store every
// call to any method called Render.
if selExpr.Sel.Name != "Render" {
return true
}
// Add this call's args to the renderArgs.
pos := p.Fset.Position(callExpr.Lparen)
methodCall := &model.MethodCall{
Line: pos.Line,
Names: []string{},
}
for _, arg := range callExpr.Args {
argIdent, ok := arg.(*ast.Ident)
if !ok {
continue
}
methodCall.Names = append(methodCall.Names, argIdent.Name)
}
method.RenderCalls = append(method.RenderCalls, methodCall)
return true
})
var recvType = funcDecl.Recv.List[0].Type
if recvStarType, ok := recvType.(*ast.StarExpr); ok {
recvTypeName = recvStarType.X.(*ast.Ident).Name
} else {
recvTypeName = recvType.(*ast.Ident).Name
}
return
}
func (s *SourceInfoProcessor) getControllerSpec(spec *ast.TypeSpec, p *packages.Package, localImportMap map[string]string) (controllerSpec *model.TypeInfo) {
structType := spec.Type.(*ast.StructType)
// At this point we know it's a type declaration for a struct.
// Fill in the rest of the info by diving into the fields.
// Add it provisionally to the Controller list -- it's later filtered using field info.
controllerSpec = &model.TypeInfo{
StructName: spec.Name.Name,
ImportPath: p.PkgPath,
PackageName: p.Name,
}
log := s.sourceProcessor.log.New("file", p.Fset.Position(spec.Pos()).Filename, "position", p.Fset.Position(spec.Pos()).Line)
for _, field := range structType.Fields.List {
// If field.Names is set, it's not an embedded type.
if field.Names != nil {
continue
}
// A direct "sub-type" has an ast.Field as either:
// Ident { "AppController" }
// SelectorExpr { "rev", "Controller" }
// Additionally, that can be wrapped by StarExprs.
fieldType := field.Type
pkgName, typeName := func() (string, string) {
// Drill through any StarExprs.
for {
if starExpr, ok := fieldType.(*ast.StarExpr); ok {
fieldType = starExpr.X
continue
}
break
}
// If the embedded type is in the same package, it's an Ident.
if ident, ok := fieldType.(*ast.Ident); ok {
return "", ident.Name
}
if selectorExpr, ok := fieldType.(*ast.SelectorExpr); ok {
if pkgIdent, ok := selectorExpr.X.(*ast.Ident); ok {
return pkgIdent.Name, selectorExpr.Sel.Name
}
}
return "", ""
}()
// If a typename wasn't found, skip it.
if typeName == "" {
continue
}
// Find the import path for this type.
// If it was referenced without a package name, use the current package import path.
// Else, look up the package's import path by name.
var importPath string
if pkgName == "" {
importPath = p.PkgPath
} else {
var ok bool
if importPath, ok = localImportMap[pkgName]; !ok {
log.Debug("Debug: Unusual, failed to find package locally ", "package", pkgName, "type", typeName, "map", s.sourceProcessor.importMap, "usedin", )
if importPath, ok = s.sourceProcessor.importMap[pkgName]; !ok {
log.Error("Error: Failed to find import path for ", "package", pkgName, "type", typeName, "map", s.sourceProcessor.importMap, "usedin", )
continue
}
}
}
controllerSpec.EmbeddedTypes = append(controllerSpec.EmbeddedTypes, &model.EmbeddedTypeName{
ImportPath: importPath,
StructName: typeName,
})
}
s.sourceProcessor.log.Info("Added controller spec", "name", controllerSpec.StructName, "package", controllerSpec.ImportPath)
return
}
func (s *SourceInfoProcessor) getStructTypeDecl(decl ast.Decl, fset *token.FileSet) (spec *ast.TypeSpec, found bool) {
genDecl, ok := decl.(*ast.GenDecl)
if !ok {
return
}
if genDecl.Tok != token.TYPE {
return
}
if len(genDecl.Specs) == 0 {
utils.Logger.Warn("Warn: Surprising: %s:%d Decl contains no specifications", fset.Position(decl.Pos()).Filename, fset.Position(decl.Pos()).Line)
return
}
spec = genDecl.Specs[0].(*ast.TypeSpec)
_, found = spec.Type.(*ast.StructType)
return
}
func (s *SourceInfoProcessor) getFuncName(funcDecl *ast.FuncDecl) string {
prefix := ""
if funcDecl.Recv != nil {
recvType := funcDecl.Recv.List[0].Type
if recvStarType, ok := recvType.(*ast.StarExpr); ok {
prefix = "(*" + recvStarType.X.(*ast.Ident).Name + ")"
} else {
prefix = recvType.(*ast.Ident).Name
}
prefix += "."
}
return prefix + funcDecl.Name.Name
}
func (s *SourceInfoProcessor) addImport(decl ast.Decl, p *packages.Package, localImportMap map[string]string, log logger.MultiLogger) (shouldContinue bool) {
shouldContinue = true
genDecl, ok := decl.(*ast.GenDecl)
if !ok {
return
}
if genDecl.Tok == token.IMPORT {
shouldContinue = false
for _, spec := range genDecl.Specs {
importSpec := spec.(*ast.ImportSpec)
//fmt.Printf("*** import specification %#v\n", importSpec)
var pkgAlias string
if importSpec.Name != nil {
pkgAlias = importSpec.Name.Name
if pkgAlias == "_" {
continue
}
}
quotedPath := importSpec.Path.Value // e.g. "\"sample/app/models\""
fullPath := quotedPath[1 : len(quotedPath) - 1] // Remove the quotes
if pkgAlias == "" {
pkgAlias = fullPath
if index := strings.LastIndex(pkgAlias, "/"); index > 0 {
pkgAlias = pkgAlias[index + 1:]
}
}
localImportMap[pkgAlias] = fullPath
}
}
return
}

View File

@@ -0,0 +1,310 @@
package parser2
import (
"github.com/revel/cmd/logger"
"github.com/revel/cmd/model"
"github.com/revel/cmd/utils"
"go/ast"
"go/parser"
"go/scanner"
"go/token"
"golang.org/x/tools/go/packages"
"os"
"path/filepath"
"strings"
)
type (
SourceProcessor struct {
revelContainer *model.RevelContainer
log logger.MultiLogger
packageList []*packages.Package
importMap map[string]string
packageMap map[string]string
sourceInfoProcessor *SourceInfoProcessor
sourceInfo *model.SourceInfo
}
)
func ProcessSource(revelContainer *model.RevelContainer) (sourceInfo *model.SourceInfo, compileError error) {
utils.Logger.Info("ProcessSource")
processor := NewSourceProcessor(revelContainer)
compileError = processor.parse()
sourceInfo = processor.sourceInfo
if compileError == nil {
processor.log.Infof("From parsers : Structures:%d InitImports:%d ValidationKeys:%d %v", len(sourceInfo.StructSpecs), len(sourceInfo.InitImportPaths), len(sourceInfo.ValidationKeys), sourceInfo.PackageMap)
}
return
}
func NewSourceProcessor(revelContainer *model.RevelContainer) *SourceProcessor {
s := &SourceProcessor{revelContainer:revelContainer, log:utils.Logger.New("parser", "SourceProcessor")}
s.sourceInfoProcessor = NewSourceInfoProcessor(s)
return s
}
func (s *SourceProcessor) parse() (compileError error) {
print("Parsing packages, (may require download if not cached)...")
if compileError = s.addPackages(); compileError != nil {
return
}
println(" Completed")
if compileError = s.addImportMap(); compileError != nil {
return
}
if compileError = s.addSourceInfo(); compileError != nil {
return
}
s.sourceInfo.PackageMap = map[string]string{}
getImportFromMap := func(packagePath string) string {
for path := range s.packageMap {
if strings.Index(path, packagePath) == 0 {
fullPath := s.packageMap[path]
return fullPath[:(len(fullPath) - len(path) + len(packagePath))]
}
}
return ""
}
s.sourceInfo.PackageMap[model.RevelImportPath] = getImportFromMap(model.RevelImportPath)
s.sourceInfo.PackageMap[s.revelContainer.ImportPath] = getImportFromMap(s.revelContainer.ImportPath)
for _, module := range s.revelContainer.ModulePathMap {
s.sourceInfo.PackageMap[module.ImportPath] = getImportFromMap(module.ImportPath)
}
return
}
// 这两个方法来自util
// Shortcut to fsWalk
func (s *SourceProcessor) Walk(root string, walkFn filepath.WalkFunc) error {
return s.fsWalk(root, root, walkFn)
}
// Walk the path tree using the function
// Every file found will call the function
func (s *SourceProcessor) fsWalk(fname string, linkName string, walkFn filepath.WalkFunc) error {
fsWalkFunc := func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
var name string
name, err = filepath.Rel(fname, path)
if err != nil {
return err
}
path = filepath.Join(linkName, name)
// 改了这里
if strings.Contains(path, "/leanote/public") ||
strings.Contains(path, "/leanote/files") ||
strings.Contains(path, "/leanote/doc") ||
strings.Contains(path, "/leanote/logs") ||
strings.Contains(path, "/leanote/build") ||
strings.Contains(path, "/leanote/target") {
s.log.Warn("public 或 files 不要处理", "path", path)
return filepath.SkipDir
}
if err == nil && info.Mode() & os.ModeSymlink == os.ModeSymlink {
var symlinkPath string
symlinkPath, err = filepath.EvalSymlinks(path)
if err != nil {
return err
}
// https://github.com/golang/go/blob/master/src/path/filepath/path.go#L392
info, err = os.Lstat(symlinkPath)
if err != nil {
return walkFn(path, info, err)
}
if info.IsDir() {
return s.fsWalk(symlinkPath, path, walkFn)
}
}
return walkFn(path, info, err)
}
err := filepath.Walk(fname, fsWalkFunc)
return err
}
// Using the packages.Load function load all the packages and type specifications (forces compile).
// this sets the SourceProcessor.packageList []*packages.Package
func (s *SourceProcessor) addPackages() (err error) {
allPackages := []string{model.RevelImportPath + "/..."}
for _, module := range s.revelContainer.ModulePathMap {
allPackages = append(allPackages, module.ImportPath + "/...") // +"/app/controllers/...")
}
s.log.Info("Reading packages", "packageList", allPackages)
//allPackages = []string{s.revelContainer.ImportPath + "/..."} //+"/app/controllers/..."}
config := &packages.Config{
// ode: packages.NeedSyntax | packages.NeedCompiledGoFiles,
Mode:
packages.NeedTypes | // For compile error
packages.NeedDeps | // To load dependent files
packages.NeedName | // Loads the full package name
packages.NeedSyntax, // To load ast tree (for end points)
//Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles |
// packages.NeedImports | packages.NeedDeps | packages.NeedExportsFile |
// packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo |
// packages.NeedTypesSizes,
//Mode: packages.NeedName | packages.NeedImports | packages.NeedDeps | packages.NeedExportsFile | packages.NeedFiles |
// packages.NeedCompiledGoFiles | packages.NeedTypesSizes |
// packages.NeedSyntax | packages.NeedCompiledGoFiles ,
//Mode: packages.NeedSyntax | packages.NeedCompiledGoFiles | packages.NeedName | packages.NeedFiles |
// packages.LoadTypes | packages.NeedTypes | packages.NeedDeps, //, // |
// packages.NeedTypes, // packages.LoadTypes | packages.NeedSyntax | packages.NeedTypesInfo,
//packages.LoadSyntax | packages.NeedDeps,
Dir:s.revelContainer.AppPath,
}
s.packageList, err = packages.Load(config, allPackages...)
s.log.Info("Loaded modules ", "len results", len(s.packageList), "error", err)
// Now process the files in the aap source folder s.revelContainer.ImportPath + "/...",
err = s.Walk(s.revelContainer.BasePath, s.processPath)
s.log.Info("Loaded apps and modules ", "len results", len(s.packageList), "error", err)
return
}
// This callback is used to build the packages for the "app" package. This allows us to
// parse the source files without doing a full compile on them
// This callback only processes folders, so any files passed to this will return a nil
func (s *SourceProcessor) processPath(path string, info os.FileInfo, err error) error {
if err != nil {
s.log.Error("Error scanning app source:", "error", err)
return nil
}
// Ignore files and folders not marked tmp (since those are generated)
if !info.IsDir() || info.Name() == "tmp" {
return nil
}
// Real work for processing the folder
pkgImportPath := s.revelContainer.ImportPath
appPath := s.revelContainer.BasePath
if appPath != path {
pkgImportPath = s.revelContainer.ImportPath + "/" + filepath.ToSlash(path[len(appPath) + 1:])
}
s.log.Info("Processing source package folder", "package", pkgImportPath, "path", path)
// Parse files within the path.
var pkgMap map[string]*ast.Package
fset := token.NewFileSet()
pkgMap, err = parser.ParseDir(
fset,
path,
func(f os.FileInfo) bool {
return !f.IsDir() && !strings.HasPrefix(f.Name(), ".") && strings.HasSuffix(f.Name(), ".go")
},
0)
if err != nil {
if errList, ok := err.(scanner.ErrorList); ok {
var pos = errList[0].Pos
newError := &utils.SourceError{
SourceType: ".go source",
Title: "Go Compilation Error",
Path: pos.Filename,
Description: errList[0].Msg,
Line: pos.Line,
Column: pos.Column,
SourceLines: utils.MustReadLines(pos.Filename),
}
errorLink := s.revelContainer.Config.StringDefault("error.link", "")
if errorLink != "" {
newError.SetLink(errorLink)
}
return newError
}
// This is exception, err already checked above. Here just a print
ast.Print(nil, err)
s.log.Fatal("Failed to parse dir", "error", err)
}
// Skip "main" packages.
delete(pkgMap, "main")
// Ignore packages that end with _test
// These cannot be included in source code that is not generated specifically as a test
for i := range pkgMap {
if len(i) > 6 {
if string(i[len(i) - 5:]) == "_test" {
delete(pkgMap, i)
}
}
}
// If there is no code in this directory, skip it.
if len(pkgMap) == 0 {
return nil
}
// There should be only one package in this directory.
if len(pkgMap) > 1 {
for i := range pkgMap {
println("Found duplicate packages in single directory ", i)
}
utils.Logger.Fatal("Most unexpected! Multiple packages in a single directory:", "packages", pkgMap)
}
// At this point there is only one package in the pkgs map,
p := &packages.Package{}
p.PkgPath = pkgImportPath
p.Fset = fset
for _, pkg := range pkgMap {
p.Name = pkg.Name
s.log.Info("Found package", "pkg.Name", pkg.Name, "p.Name", p.PkgPath)
for filename, astFile := range pkg.Files {
p.Syntax = append(p.Syntax, astFile)
p.GoFiles = append(p.GoFiles, filename)
}
}
s.packageList = append(s.packageList, p)
return nil
}
// This function is used to populate a map so that we can lookup controller embedded types in order to determine
// if a Struct inherits from from revel.Controller
func (s *SourceProcessor) addImportMap() (err error) {
s.importMap = map[string]string{}
s.packageMap = map[string]string{}
for _, p := range s.packageList {
if len(p.Errors) > 0 {
// Generate a compile error
for _, e := range p.Errors {
s.log.Info("While reading packages encountered import error ignoring ", "PkgPath", p.PkgPath, "error", e)
}
}
for _, tree := range p.Syntax {
s.importMap[tree.Name.Name] = p.PkgPath
}
}
return
}
func (s *SourceProcessor) addSourceInfo() (err error) {
for _, p := range s.packageList {
if sourceInfo := s.sourceInfoProcessor.processPackage(p); sourceInfo != nil {
if s.sourceInfo != nil {
s.sourceInfo.Merge(sourceInfo)
} else {
s.sourceInfo = sourceInfo
}
}
}
return
}

151
app/cmd/revel.go Normal file
View File

@@ -0,0 +1,151 @@
// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
// Revel Framework source code and usage is governed by a MIT style
// license that can be found in the LICENSE file.
// The command line tool for running Revel apps.
package main
import (
"flag"
"fmt"
"math/rand"
"os"
"runtime"
"strings"
"time"
"github.com/jessevdk/go-flags"
"github.com/agtorre/gocolorize"
"github.com/revel/cmd/logger"
"github.com/revel/cmd/model"
"github.com/revel/cmd/utils"
"bytes"
)
const (
// RevelCmdImportPath Revel framework cmd tool import path
RevelCmdImportPath = "github.com/revel/cmd"
// RevelCmdImportPath Revel framework cmd tool import path
RevelSkeletonsImportPath = "github.com/revel/skeletons"
// DefaultRunMode for revel's application
DefaultRunMode = "dev"
)
// Command structure cribbed from the genius organization of the "go" command.
type Command struct {
UpdateConfig func(c *model.CommandConfig, args []string) bool
RunWith func(c *model.CommandConfig) error
UsageLine, Short, Long string
}
// Name returns command name from usage line
func (cmd *Command) Name() string {
name := cmd.UsageLine
i := strings.Index(name, " ")
if i >= 0 {
name = name[:i]
}
return name
}
// The commands
var Commands = []*Command{
nil, // Safety net, prevent missing index from running
// 只改了这个
nil,
nil,
cmdBuild,
}
func main() {
if runtime.GOOS == "windows" {
gocolorize.SetPlain(true)
}
c := &model.CommandConfig{}
wd, _ := os.Getwd()
utils.InitLogger(wd, logger.LvlError)
parser := flags.NewParser(c, flags.HelpFlag | flags.PassDoubleDash)
if len(os.Args) < 2 {
parser.WriteHelp(os.Stdout)
os.Exit(1)
}
if err := ParseArgs(c, parser, os.Args[1:]); err != nil {
fmt.Fprint(os.Stderr, err.Error() + "\n")
os.Exit(1)
}
// Switch based on the verbose flag
if len(c.Verbose) > 1 {
utils.InitLogger(wd, logger.LvlDebug)
} else if len(c.Verbose) > 0 {
utils.InitLogger(wd, logger.LvlInfo)
} else {
utils.InitLogger(wd, logger.LvlWarn)
}
// Setup package resolver
c.InitPackageResolver()
if err := c.UpdateImportPath(); err != nil {
utils.Logger.Error(err.Error())
parser.WriteHelp(os.Stdout)
os.Exit(1)
}
command := Commands[c.Index]
println("Revel executing:", command.Short)
if err := command.RunWith(c); err != nil {
utils.Logger.Error("Unable to execute", "error", err)
os.Exit(1)
}
}
// Parse the arguments passed into the model.CommandConfig
func ParseArgs(c *model.CommandConfig, parser *flags.Parser, args []string) (err error) {
var extraArgs []string
if ini := flag.String("ini", "none", ""); *ini != "none" {
if err = flags.NewIniParser(parser).ParseFile(*ini); err != nil {
return
}
} else {
if extraArgs, err = parser.ParseArgs(args); err != nil {
return
} else {
switch parser.Active.Name {
case "new":
c.Index = model.NEW
case "run":
c.Index = model.RUN
case "build":
c.Index = model.BUILD
case "package":
c.Index = model.PACKAGE
case "clean":
c.Index = model.CLEAN
case "test":
c.Index = model.TEST
case "version":
c.Index = model.VERSION
}
}
}
if !Commands[c.Index].UpdateConfig(c, extraArgs) {
buffer := &bytes.Buffer{}
parser.WriteHelp(buffer)
err = fmt.Errorf("Invalid command line arguements %v\n%s", extraArgs, buffer.String())
}
return
}
func init() {
rand.Seed(time.Now().UnixNano())
}

View File

@@ -46,14 +46,17 @@ func (c Attach) uploadAttach(noteId string) (re info.Re) {
return re return re
} }
file, handel, err := c.Request.FormFile("file") var data []byte
if err != nil { c.Params.Bind(&data, "file")
return re
}
defer file.Close()
data, err := ioutil.ReadAll(file) // file, handel, err := c.Request.FormFile("file")
if err != nil { // if err != nil {
// return re
// }
// defer file.Close()
// data, err := ioutil.ReadAll(file)
if data == nil || len(data) == 0 {
return re return re
} }
// > 5M? // > 5M?
@@ -71,10 +74,13 @@ func (c Attach) uploadAttach(noteId string) (re info.Re) {
newGuid := NewGuid() newGuid := NewGuid()
filePath := "files/" + GetRandomFilePath(c.GetUserId(), newGuid) + "/attachs" filePath := "files/" + GetRandomFilePath(c.GetUserId(), newGuid) + "/attachs"
dir := revel.BasePath + "/" + filePath dir := revel.BasePath + "/" + filePath
err = os.MkdirAll(dir, 0755) err := os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
return re return re
} }
handel := c.Params.Files["file"][0]
// 生成新的文件名 // 生成新的文件名
filename := handel.Filename filename := handel.Filename
_, ext := SplitFilename(filename) // .doc _, ext := SplitFilename(filename) // .doc

View File

@@ -27,7 +27,7 @@ func (c *BaseController) Message(message string, args ...interface{}) (value str
func (c BaseController) GetUserId() string { func (c BaseController) GetUserId() string {
if userId, ok := c.Session["UserId"]; ok { if userId, ok := c.Session["UserId"]; ok {
return userId return userId.(string)
} }
return "" return ""
} }
@@ -47,51 +47,30 @@ func (c BaseController) GetObjectUserId() bson.ObjectId {
func (c BaseController) GetEmail() string { func (c BaseController) GetEmail() string {
if email, ok := c.Session["Email"]; ok { if email, ok := c.Session["Email"]; ok {
return email return email.(string)
} }
return "" return ""
} }
func (c BaseController) GetUsername() string { func (c BaseController) GetUsername() string {
if email, ok := c.Session["Username"]; ok { if email, ok := c.Session["Username"]; ok {
return email return email.(string)
} }
return "" return ""
} }
// 得到用户信息 // 得到用户信息
func (c BaseController) GetUserInfo() info.User { func (c BaseController) GetUserInfo() info.User {
if userId, ok := c.Session["UserId"]; ok && userId != "" { userId := c.GetUserId()
if userId != "" {
return userService.GetUserInfo(userId) return userService.GetUserInfo(userId)
/*
notebookWidth, _ := strconv.Atoi(c.Session["NotebookWidth"])
noteListWidth, _ := strconv.Atoi(c.Session["NoteListWidth"])
mdEditorWidth, _ := strconv.Atoi(c.Session["MdEditorWidth"])
LogJ(c.Session)
user := info.User{UserId: bson.ObjectIdHex(userId),
Email: c.Session["Email"],
Logo: c.Session["Logo"],
Username: c.Session["Username"],
UsernameRaw: c.Session["UsernameRaw"],
Theme: c.Session["Theme"],
NotebookWidth: notebookWidth,
NoteListWidth: noteListWidth,
MdEditorWidth: mdEditorWidth,
}
if c.Session["Verified"] == "1" {
user.Verified = true
}
if c.Session["LeftIsMin"] == "1" {
user.LeftIsMin = true
}
return user
*/
} }
return info.User{} return info.User{}
} }
func (c BaseController) GetUserAndBlogUrl() info.UserAndBlogUrl { func (c BaseController) GetUserAndBlogUrl() info.UserAndBlogUrl {
if userId, ok := c.Session["UserId"]; ok && userId != "" { userId := c.GetUserId()
if userId != "" {
return userService.GetUserAndBlogUrl(userId) return userService.GetUserAndBlogUrl(userId)
} }
return info.UserAndBlogUrl{} return info.UserAndBlogUrl{}
@@ -103,7 +82,7 @@ func (c BaseController) GetSession(key string) string {
if !ok { if !ok {
v = "" v = ""
} }
return v return v.(string)
} }
func (c BaseController) SetSession(userInfo info.User) { func (c BaseController) SetSession(userInfo info.User) {
if userInfo.UserId.Hex() != "" { if userInfo.UserId.Hex() != "" {

View File

@@ -6,7 +6,7 @@ import (
// "encoding/json" // "encoding/json"
"fmt" "fmt"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea" // . "github.com/leanote/leanote/app/lea"
"github.com/leanote/leanote/app/lea/blog" "github.com/leanote/leanote/app/lea/blog"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
// "github.com/leanote/leanote/app/types" // "github.com/leanote/leanote/app/types"
@@ -77,7 +77,7 @@ func (c Blog) e404(themePath string) revel.Result {
// life.leanote.com // life.leanote.com
// lealife.com // lealife.com
func (c Blog) domain() (ok bool, userBlog info.UserBlog) { func (c Blog) domain() (ok bool, userBlog info.UserBlog) {
host := c.Request.Request.Host // a.cc.com:9000 host := c.Request.Host // a.cc.com:9000
hostArr := strings.Split(host, ".") hostArr := strings.Split(host, ".")
if strings.Contains(host, configService.GetDefaultDomain()) { if strings.Contains(host, configService.GetDefaultDomain()) {
// 有二级域名 a.leanoe.com 3个 // 有二级域名 a.leanoe.com 3个
@@ -110,7 +110,7 @@ func (c Blog) setPreviewUrl() {
if username != "" { if username != "" {
userIdOrEmail = username userIdOrEmail = username
} }
themeId := c.Session["themeId"] themeId := c.GetSession("themeId")
theme := themeService.GetTheme(userId, themeId) theme := themeService.GetTheme(userId, themeId)
// siteUrl := configService.GetSiteUrl() // siteUrl := configService.GetSiteUrl()
@@ -609,7 +609,7 @@ func (c Blog) Post(userIdOrEmail, noteId string) (re revel.Result) {
hasDomain, userBlog := c.domain() hasDomain, userBlog := c.domain()
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
Log(err) // Log(err)
re = c.e404(userBlog.ThemePath) re = c.e404(userBlog.ThemePath)
} }
}() }()

View File

@@ -14,6 +14,7 @@ import (
// "path" // "path"
// "strconv" // "strconv"
"net/http" "net/http"
"io"
) )
// 验证码服务 // 验证码服务
@@ -30,9 +31,10 @@ func (r Ca) Apply(req *revel.Request, resp *revel.Response) {
func (c Captcha) Get() revel.Result { func (c Captcha) Get() revel.Result {
c.Response.ContentType = "image/png" c.Response.ContentType = "image/png"
image, str := captcha.Fetch() image, str := captcha.Fetch()
image.WriteTo(c.Response.Out) out := io.Writer(c.Response.GetWriter())
image.WriteTo(out)
sessionId := c.Session["_ID"] sessionId := c.GetSession("_ID")
// LogJ(c.Session) // LogJ(c.Session)
// Log("------") // Log("------")
// Log(str) // Log(str)

View File

@@ -97,11 +97,27 @@ func (c File) uploadImage(from, albumId string) (re info.Re) {
re.Ok = Ok re.Ok = Ok
}() }()
file, handel, err := c.Request.FormFile("file") // file, handel, err := c.Request.FormFile("file")
if err != nil { // if err != nil {
// return re
// }
// defer file.Close()
var data []byte
c.Params.Bind(&data, "file")
handel := c.Params.Files["file"][0]
if data == nil || len(data) == 0 {
return re return re
} }
defer file.Close()
// file, handel, err := c.Request.FormFile("file")
// if err != nil {
// return re
// }
// defer file.Close()
// data, err := ioutil.ReadAll(file)
// 生成上传路径 // 生成上传路径
newGuid := NewGuid() newGuid := NewGuid()
@@ -116,7 +132,7 @@ func (c File) uploadImage(from, albumId string) (re info.Re) {
} }
dir := revel.BasePath + "/" + fileUrlPath dir := revel.BasePath + "/" + fileUrlPath
err = os.MkdirAll(dir, 0755) err := os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
return re return re
} }
@@ -136,11 +152,11 @@ func (c File) uploadImage(from, albumId string) (re info.Re) {
} }
filename = newGuid + ext filename = newGuid + ext
data, err := ioutil.ReadAll(file) // data, err := ioutil.ReadAll(file)
if err != nil { // if err != nil {
LogJ(err) // LogJ(err)
return re // return re
} // }
var maxFileSize float64 var maxFileSize float64
if from == "logo" { if from == "logo" {

View File

@@ -11,6 +11,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"time" "time"
"runtime"
// "github.com/leanote/leanote/app/types" // "github.com/leanote/leanote/app/types"
// "io/ioutil" // "io/ioutil"
"fmt" "fmt"
@@ -233,20 +234,21 @@ func (c Note) UpdateNoteOrContent(noteOrContent info.NoteOrContent) revel.Result
} }
//------------- //-------------
afterContentUsn := 0 // afterContentUsn := 0
contentOk := false // contentOk := false
contentMsg := "" // contentMsg := ""
if c.Has("Content") { if c.Has("Content") {
// noteService.UpdateNoteContent(noteOrContent.UserId, c.GetUserId(), // noteService.UpdateNoteContent(noteOrContent.UserId, c.GetUserId(),
// noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract) // noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract)
contentOk, contentMsg, afterContentUsn = noteService.UpdateNoteContent(c.GetUserId(), // contentOk, contentMsg, afterContentUsn =
noteService.UpdateNoteContent(c.GetUserId(),
noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract, noteOrContent.NoteId, noteOrContent.Content, noteOrContent.Abstract,
needUpdateNote, -1, time.Now()) needUpdateNote, -1, time.Now())
} }
Log(afterContentUsn) // Log("usn", "afterContentUsn", afterContentUsn + "")
Log(contentOk) // Log(contentOk)
Log(contentMsg) // Log(contentMsg)
return c.RenderJSON(true) return c.RenderJSON(true)
} }
@@ -446,7 +448,11 @@ func (c Note) ExportPdf(noteId string) revel.Result {
binPath := configService.GetGlobalStringConfig("exportPdfBinPath") binPath := configService.GetGlobalStringConfig("exportPdfBinPath")
// 默认路径 // 默认路径
if binPath == "" { if binPath == "" {
binPath = "/usr/local/bin/wkhtmltopdf" if runtime.GOOS == "windows" {
binPath = `C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe`
} else {
binPath = "/usr/local/bin/wkhtmltopdf"
}
} }
url := configService.GetSiteUrl() + "/note/toPdf?noteId=" + noteId + "&appKey=" + appKey url := configService.GetSiteUrl() + "/note/toPdf?noteId=" + noteId + "&appKey=" + appKey
@@ -456,13 +462,29 @@ func (c Note) ExportPdf(noteId string) revel.Result {
// http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf_0.10.0_rc2-doc.html // http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf_0.10.0_rc2-doc.html
// wkhtmltopdf参数大全 // wkhtmltopdf参数大全
var cc string var cc string
// var cc []string
var ccWindows []string
if note.IsMarkdown { if note.IsMarkdown {
cc = binPath + " --lowquality --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\"" cc = binPath + " --lowquality --window-status done \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
// cc = []string{binPath, "--lowquality", "--window-status", "done", "\"" + url + "\"", "\"" + path + "\""}
ccWindows = []string{"/C", binPath, "--lowquality", "--window-status", "done", url, path}
} else { } else {
cc = binPath + " --lowquality \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\"" cc = binPath + " --lowquality \"" + url + "\" \"" + path + "\"" // \"" + cookieDomain + "\" \"" + cookieName + "\" \"" + cookieValue + "\""
// cc = []string{binPath, "--lowquality", "\"" + url + "\"", "\"" + path + "\""}
ccWindows = []string{"/C", binPath, "--lowquality", url, path}
} }
cmd := exec.Command("/bin/sh", "-c", cc) var cmd *exec.Cmd
// fmt.Println("-------1", runtime.GOOS, ccWindows)
if runtime.GOOS == "windows" {
fmt.Println(ccWindows)
// cmd = exec.Command("cmd", ccWindows...)
cmd = exec.Command(ccWindows[1], ccWindows[2:]...)
} else {
fmt.Println(cc)
cmd = exec.Command("/bin/sh", "-c", cc)
}
_, err := cmd.Output() _, err := cmd.Output()
if err != nil { if err != nil {
return c.RenderText("export pdf error. " + fmt.Sprintf("%v", err)) return c.RenderText("export pdf error. " + fmt.Sprintf("%v", err))

View File

@@ -25,7 +25,7 @@ func (c Preview) getPreviewThemeAbsolutePath(themeId string) bool {
if themeId != "" { if themeId != "" {
c.Session["themeId"] = themeId // 存到session中, 下次的url就不能带了, 待优化, 有时会取不到 c.Session["themeId"] = themeId // 存到session中, 下次的url就不能带了, 待优化, 有时会取不到
} else { } else {
themeId = c.Session["themeId"] // 直接从session中获取 themeId = c.GetSession("themeId") // 直接从session中获取
} }
if themeId == "" { if themeId == "" {
return false return false

View File

@@ -83,7 +83,7 @@ func AuthInterceptor(c *revel.Controller) revel.Result {
// 验证是否已登录 // 验证是否已登录
// 必须是管理员 // 必须是管理员
if username, ok := c.Session["Username"]; ok && username == configService.GetAdminUsername() { if username, ok := c.Session["Username"]; ok && username.(string) == configService.GetAdminUsername() {
return nil // 已登录 return nil // 已登录
} }

View File

@@ -23,18 +23,18 @@ type ApiBaseContrller struct {
// 得到token, 这个token是在AuthInterceptor设到Session中的 // 得到token, 这个token是在AuthInterceptor设到Session中的
func (c ApiBaseContrller) getToken() string { func (c ApiBaseContrller) getToken() string {
return c.Session["_token"] return c.GetSession("_token")
} }
// userId // userId
// _userId是在AuthInterceptor设置的 // _userId是在AuthInterceptor设置的
func (c ApiBaseContrller) getUserId() string { func (c ApiBaseContrller) getUserId() string {
return c.Session["_userId"] return c.GetSession("_userId")
} }
// 得到用户信息 // 得到用户信息
func (c ApiBaseContrller) getUserInfo() info.User { func (c ApiBaseContrller) getUserInfo() info.User {
userId := c.Session["_userId"] userId := c.GetSession("_userId")
if userId == "" { if userId == "" {
return info.User{} return info.User{}
} }
@@ -53,16 +53,23 @@ func (c ApiBaseContrller) uploadAttach(name string, noteId string) (ok bool, msg
} }
*/ */
file, handel, err := c.Request.FormFile(name) var data []byte
if err != nil { c.Params.Bind(&data, name)
handel := c.Params.Files[name][0]
if data == nil || len(data) == 0 {
return return
} }
defer file.Close()
data, err := ioutil.ReadAll(file) // file, handel, err := c.Request.FormFile(name)
if err != nil { // if err != nil {
return // return
} // }
// defer file.Close()
// data, err := ioutil.ReadAll(file)
// if err != nil {
// return
// }
// > 5M? // > 5M?
maxFileSize := configService.GetUploadSize("uploadAttachSize") maxFileSize := configService.GetUploadSize("uploadAttachSize")
if maxFileSize <= 0 { if maxFileSize <= 0 {
@@ -79,7 +86,7 @@ func (c ApiBaseContrller) uploadAttach(name string, noteId string) (ok bool, msg
filePath := "files/" + GetRandomFilePath(userId, newGuid) + "/attachs" filePath := "files/" + GetRandomFilePath(userId, newGuid) + "/attachs"
dir := revel.BasePath + "/" + filePath dir := revel.BasePath + "/" + filePath
err = os.MkdirAll(dir, 0755) err := os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
return return
} }
@@ -122,11 +129,18 @@ func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok
if isAttach { if isAttach {
return c.uploadAttach(name, noteId) return c.uploadAttach(name, noteId)
} }
file, handel, err := c.Request.FormFile(name) // file, handel, err := c.Request.FormFile(name)
if err != nil { // if err != nil {
// return
// }
// defer file.Close()
var data []byte
c.Params.Bind(&data, name)
handel := c.Params.Files[name][0]
if data == nil || len(data) == 0 {
return return
} }
defer file.Close()
newGuid := NewGuid() newGuid := NewGuid()
// 生成上传路径 // 生成上传路径
@@ -135,7 +149,7 @@ func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok
fileUrlPath := "files/" + GetRandomFilePath(userId, newGuid) + "/images" fileUrlPath := "files/" + GetRandomFilePath(userId, newGuid) + "/images"
dir := revel.BasePath + "/" + fileUrlPath dir := revel.BasePath + "/" + fileUrlPath
err = os.MkdirAll(dir, 0755) err := os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
return return
} }
@@ -148,10 +162,10 @@ func (c ApiBaseContrller) upload(name string, noteId string, isAttach bool) (ok
// } // }
filename = newGuid + ext filename = newGuid + ext
data, err := ioutil.ReadAll(file) // data, err := ioutil.ReadAll(file)
if err != nil { // if err != nil {
return // return
} // }
maxFileSize := configService.GetUploadSize("uploadImageSize") maxFileSize := configService.GetUploadSize("uploadImageSize")
if maxFileSize <= 0 { if maxFileSize <= 0 {

View File

@@ -178,7 +178,7 @@ func (c ApiNote) fixPostNotecontent(noteOrContent *info.ApiNote) {
// "/api/file/getImage?fileId="+file.FileId, -1) // "/api/file/getImage?fileId="+file.FileId, -1)
} else { } else {
reg, _ := regexp.Compile(`https*://[^/]*?/api/file/getAttach\?fileId=`+file.LocalFileId) reg, _ := regexp.Compile(`https*://[^/]*?/api/file/getAttach\?fileId=`+file.LocalFileId)
Log(reg) // Log(reg)
noteOrContent.Content = reg.ReplaceAllString(noteOrContent.Content, `/api/file/getAttach?fileId=`+file.FileId) noteOrContent.Content = reg.ReplaceAllString(noteOrContent.Content, `/api/file/getAttach?fileId=`+file.FileId)
/* /*
noteOrContent.Content = strings.Replace(noteOrContent.Content, noteOrContent.Content = strings.Replace(noteOrContent.Content,

View File

@@ -104,16 +104,23 @@ func (c ApiUser) uploadImage() (ok bool, msg, url string) {
var fileUrlPath = "" var fileUrlPath = ""
ok = false ok = false
file, handel, err := c.Request.FormFile("file") var data []byte
if err != nil { c.Params.Bind(&data, "file")
handel := c.Params.Files["file"][0]
if data == nil || len(data) == 0 {
return return
} }
defer file.Close()
// file, handel, err := c.Request.FormFile("file")
// if err != nil {
// return
// }
// defer file.Close()
// 生成上传路径 // 生成上传路径
fileUrlPath = "public/upload/" + c.getUserId() + "/images/logo" fileUrlPath = "public/upload/" + c.getUserId() + "/images/logo"
dir := revel.BasePath + "/" + fileUrlPath dir := revel.BasePath + "/" + fileUrlPath
err = os.MkdirAll(dir, 0755) err := os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
return return
} }
@@ -129,11 +136,11 @@ func (c ApiUser) uploadImage() (ok bool, msg, url string) {
} }
filename = NewGuid() + ext filename = NewGuid() + ext
data, err := ioutil.ReadAll(file) // data, err := ioutil.ReadAll(file)
if err != nil { // if err != nil {
LogJ(err) // LogJ(err)
return // return
} // }
// > 5M? // > 5M?
if len(data) > 5*1024*1024 { if len(data) > 5*1024*1024 {

View File

@@ -90,7 +90,10 @@ func AuthInterceptor(c *revel.Controller) revel.Result {
if noToken && userId == "" { if noToken && userId == "" {
// 从session中获取, api/file/getImage, api/file/getAttach, api/file/getAllAttach // 从session中获取, api/file/getImage, api/file/getAttach, api/file/getAllAttach
// 客户端 // 客户端
userId, _ = c.Session["UserId"] userIdI, _ := c.Session["UserId"]
if userIdI != nil {
userId = userIdI.(string)
}
} }
c.Session["_userId"] = userId c.Session["_userId"] = userId

View File

@@ -351,14 +351,21 @@ func (c MemberBlog) uploadImage(themeId string) (re info.Re) {
re.Ok = Ok re.Ok = Ok
}() }()
file, handel, err := c.Request.FormFile("file") var data []byte
if err != nil { c.Params.Bind(&data, "file")
handel := c.Params.Files["file"][0]
if data == nil || len(data) == 0 {
return re return re
} }
defer file.Close()
// file, handel, err := c.Request.FormFile("file")
// if err != nil {
// return re
// }
// defer file.Close()
// 生成上传路径 // 生成上传路径
dir := themeService.GetThemeAbsolutePath(c.GetUserId(), themeId) + "/images" dir := themeService.GetThemeAbsolutePath(c.GetUserId(), themeId) + "/images"
err = os.MkdirAll(dir, 0755) err := os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
return re return re
} }
@@ -374,11 +381,11 @@ func (c MemberBlog) uploadImage(themeId string) (re info.Re) {
} }
filename = filename filename = filename
data, err := ioutil.ReadAll(file) // data, err := ioutil.ReadAll(file)
if err != nil { // if err != nil {
LogJ(err) // LogJ(err)
return re // return re
} // }
// > 2M? // > 2M?
if len(data) > 5*1024*1024 { if len(data) > 5*1024*1024 {
@@ -441,17 +448,24 @@ func (c MemberBlog) ExportTheme(themeId string) revel.Result {
func (c MemberBlog) ImportTheme() revel.Result { func (c MemberBlog) ImportTheme() revel.Result {
re := info.NewRe() re := info.NewRe()
file, handel, err := c.Request.FormFile("file") var data []byte
if err != nil { c.Params.Bind(&data, "file")
re.Msg = fmt.Sprintf("%v", err) handel := c.Params.Files["file"][0]
if data == nil || len(data) == 0 {
return c.RenderJSON(re) return c.RenderJSON(re)
} }
defer file.Close() // file, handel, err := c.Request.FormFile("file")
// if err != nil {
// re.Msg = fmt.Sprintf("%v", err)
// return c.RenderJSON(re)
// }
// defer file.Close()
// 生成上传路径 // 生成上传路径
userId := c.GetUserId() userId := c.GetUserId()
dir := revel.BasePath + "/public/upload/" + userId + "/tmp" dir := revel.BasePath + "/public/upload/" + userId + "/tmp"
err = os.MkdirAll(dir, 0755) err := os.MkdirAll(dir, 0755)
if err != nil { if err != nil {
re.Msg = fmt.Sprintf("%v", err) re.Msg = fmt.Sprintf("%v", err)
return c.RenderJSON(re) return c.RenderJSON(re)
@@ -467,10 +481,10 @@ func (c MemberBlog) ImportTheme() revel.Result {
} }
filename = filename filename = filename
data, err := ioutil.ReadAll(file) // data, err := ioutil.ReadAll(file)
if err != nil { // if err != nil {
return c.RenderJSON(re) // return c.RenderJSON(re)
} // }
// > 10M? // > 10M?
if len(data) > 10*1024*1024 { if len(data) > 10*1024*1024 {

View File

@@ -76,6 +76,11 @@ func Init(url, dbname string) {
// get dbname from urlEnv // get dbname from urlEnv
urls := strings.Split(url, "/") urls := strings.Split(url, "/")
dbname = urls[len(urls)-1] dbname = urls[len(urls)-1]
if strings.Contains(dbname, "?") {
urls = strings.Split(dbname, "?")
dbname = urls[0]
}
} }
} }
if dbname == "" { if dbname == "" {

View File

@@ -6,21 +6,21 @@ import (
"github.com/revel/revel" "github.com/revel/revel"
) )
func Log(i ...interface{}) { func Log(msg string, i ...interface{}) {
revel.INFO.Println(i...) revel.AppLog.Info(msg, i...)
} }
func LogW(i ...interface{}) { func Logf(msg string, i ...interface{}) {
revel.WARN.Println(i...) revel.AppLog.Infof(msg, i...)
} }
func Log1(key, i interface{}) { func LogW(msg string, i ...interface{}) {
revel.INFO.Println(key, i) revel.AppLog.Warn(msg, i...)
} }
func LogJ(i interface{}) { func LogJ(i interface{}) {
b, _ := json.MarshalIndent(i, "", " ") b, _ := json.MarshalIndent(i, "", " ")
revel.INFO.Println(string(b)) revel.AppLog.Info(string(b))
} }
// 为test用 // 为test用

View File

@@ -79,7 +79,6 @@ func SendEmailOld(to, subject, body string) bool {
err := smtp.SendMail(host+":"+port, auth, username, send_to, msg) err := smtp.SendMail(host+":"+port, auth, username, send_to, msg)
if err != nil { if err != nil {
Log(err)
return false return false
} }
return true return true

View File

@@ -183,7 +183,6 @@ func PutFileStrContent(path, content string) bool {
// Log(path) // Log(path)
if err1 != nil { if err1 != nil {
Log(err1)
return false return false
} }
return true return true

View File

@@ -400,9 +400,6 @@ func FixFilename(filename string) string {
func IsValidTime(t time.Time) bool { func IsValidTime(t time.Time) bool {
if t.Year() > 20 { if t.Year() > 20 {
now := time.Now() now := time.Now()
Log("------")
Log(t)
Log(now)
if t.Before(now) { if t.Before(now) {
return true return true
} }

View File

@@ -104,7 +104,7 @@ func (r *RenderTemplateResult) Apply(req *revel.Request, resp *revel.Response) {
chunked := revel.Config.BoolDefault("results.chunked", false) chunked := revel.Config.BoolDefault("results.chunked", false)
// If it's a HEAD request, throw away the bytes. // If it's a HEAD request, throw away the bytes.
out := io.Writer(resp.Out) out := io.Writer(resp.GetWriter())
if req.Method == "HEAD" { if req.Method == "HEAD" {
out = ioutil.Discard out = ioutil.Discard
} }
@@ -289,7 +289,7 @@ func (r ErrorResult) Apply(req *revel.Request, resp *revel.Response) {
// 不是preview就不要显示错误了 // 不是preview就不要显示错误了
if r.IsPreview { if r.IsPreview {
var b bytes.Buffer var b bytes.Buffer
out := io.Writer(resp.Out) out := io.Writer(resp.GetWriter())
// out = ioutil.Discard // out = ioutil.Discard
err = tmpl.Execute(&b, r.ViewArgs) err = tmpl.Execute(&b, r.ViewArgs)
resp.WriteHeader(http.StatusOK, "text/html; charset=utf-8") resp.WriteHeader(http.StatusOK, "text/html; charset=utf-8")

View File

@@ -8,6 +8,7 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
. "github.com/leanote/leanote/app/lea"
) )
const ( const (
@@ -146,15 +147,15 @@ func loadMessageFile(locale string, path string, info os.FileInfo, osError error
// If we have already parsed a message file for this locale, merge both // If we have already parsed a message file for this locale, merge both
if _, exists := messages[locale]; exists { if _, exists := messages[locale]; exists {
messages[locale].Merge(config) messages[locale].Merge(config)
revel.TRACE.Printf("Successfully merged messages for locale '%s'", locale) Logf("Successfully merged messages for locale '%s'", locale)
} else { } else {
messages[locale] = config messages[locale] = config
} }
revel.TRACE.Println("Successfully loaded messages from file", info.Name()) Logf("Successfully loaded messages from file", info.Name())
} }
} else { } else {
revel.TRACE.Printf("Ignoring file %s because it did not have a valid extension", info.Name()) Logf("Ignoring file %s because it did not have a valid extension", info.Name())
} }
return nil return nil
@@ -178,13 +179,13 @@ func init() {
func I18nFilter(c *revel.Controller, fc []revel.Filter) { func I18nFilter(c *revel.Controller, fc []revel.Filter) {
if foundCookie, cookieValue := hasLocaleCookie(c.Request); foundCookie { if foundCookie, cookieValue := hasLocaleCookie(c.Request); foundCookie {
revel.TRACE.Printf("Found locale cookie value: %s", cookieValue) // revel.TRACE.Printf("Found locale cookie value: %s", cookieValue)
setCurrentLocaleControllerArguments(c, cookieValue) setCurrentLocaleControllerArguments(c, cookieValue)
} else if foundHeader, headerValue := hasAcceptLanguageHeader(c.Request); foundHeader { } else if foundHeader, headerValue := hasAcceptLanguageHeader(c.Request); foundHeader {
revel.TRACE.Printf("Found Accept-Language header value: %s", headerValue) // revel.TRACE.Printf("Found Accept-Language header value: %s", headerValue)
setCurrentLocaleControllerArguments(c, headerValue) setCurrentLocaleControllerArguments(c, headerValue)
} else { } else {
revel.TRACE.Println("Unable to find locale in cookie or header, using empty string") // revel.TRACE.Println("Unable to find locale in cookie or header, using empty string")
setCurrentLocaleControllerArguments(c, "") setCurrentLocaleControllerArguments(c, "")
} }
fc[0](c, fc[1:]) fc[0](c, fc[1:])
@@ -210,12 +211,12 @@ func hasAcceptLanguageHeader(request *revel.Request) (bool, string) {
// Determine whether the given request has a valid language cookie value. // Determine whether the given request has a valid language cookie value.
func hasLocaleCookie(request *revel.Request) (bool, string) { func hasLocaleCookie(request *revel.Request) (bool, string) {
if request != nil && request.Cookies() != nil { if request != nil {
name := revel.Config.StringDefault(localeCookieConfigKey, revel.CookiePrefix+"_LANG") name := revel.Config.StringDefault(localeCookieConfigKey, revel.CookiePrefix+"_LANG")
if cookie, error := request.Cookie(name); error == nil { if cookie, error := request.Cookie(name); error == nil {
return true, cookie.Value return true, cookie.GetValue()
} else { } else {
revel.TRACE.Printf("Unable to read locale cookie with name '%s': %s", name, error.Error()) // revel.TRACE.Printf("Unable to read locale cookie with name '%s': %s", name, error.Error())
} }
} }

View File

@@ -53,7 +53,6 @@ func WriteUrl(url string, toPath string) (length int64, newFilename, path string
func GetContent(url string) (content []byte, err error) { func GetContent(url string) (content []byte, err error) {
var resp *http.Response var resp *http.Response
resp, err = http.Get(url) resp, err = http.Get(url)
Log(err)
if resp != nil && resp.Body != nil { if resp != nil && resp.Body != nil {
defer resp.Body.Close() defer resp.Body.Close()
} else { } else {
@@ -65,7 +64,6 @@ func GetContent(url string) (content []byte, err error) {
var buf []byte var buf []byte
buf, err = ioutil.ReadAll(resp.Body) buf, err = ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
Log(err)
return return
} }

View File

@@ -3,7 +3,7 @@ package route
import ( import (
"github.com/leanote/leanote/app/db" "github.com/leanote/leanote/app/db"
"github.com/revel/revel" "github.com/revel/revel"
// . "github.com/leanote/leanote/app/lea" // . "github.com/leanote/leanote/app/lea"
"net/url" "net/url"
"strings" "strings"
) )
@@ -14,15 +14,19 @@ var staticPrefix = []string{"/public", "/favicon.ico", "/css", "/js", "/images",
func RouterFilter(c *revel.Controller, fc []revel.Filter) { func RouterFilter(c *revel.Controller, fc []revel.Filter) {
// 补全controller部分 // 补全controller部分
path := c.Request.Request.URL.Path path := c.Request.URL.Path
// Figure out the Controller/Action // Figure out the Controller/Action
var route *revel.RouteMatch = revel.MainRouter.Route(c.Request.Request) // var route *revel.RouteMatch = revel.MainRouter.Route(c.Request.Request)
route := revel.MainRouter.Route(c.Request)
if route == nil { if route == nil {
c.Result = c.NotFound("No matching route found: " + c.Request.RequestURI) c.Result = c.NotFound("No matching route found: " + c.Request.GetRequestURI())
return return
} }
// Log("---------" + route.Action + " " + path)
// The route may want to explicitly return a 404. // The route may want to explicitly return a 404.
if route.Action == "404" { if route.Action == "404" {
c.Result = c.NotFound("(intentionally)") c.Result = c.NotFound("(intentionally)")
@@ -47,14 +51,23 @@ func RouterFilter(c *revel.Controller, fc []revel.Filter) {
// 检查mongodb 是否lost // 检查mongodb 是否lost
db.CheckMongoSessionLost() db.CheckMongoSessionLost()
// /api/file/getImage -> App\file (/api/file/getImage)
// App\auth
// App\note
// static\static
//
// Log("---------" + route.ControllerName + " " + path)
// api设置 // api设置
// leanote.com/api/user/get => ApiUser::Get // leanote.com/api/user/get => ApiUser::Get
//* /api/login ApiAuth.Login, 这里的设置, 其实已经转成了ApiAuth了 //* /api/login ApiAuth.Login, 这里的设置, 其实已经转成了ApiAuth了
if strings.HasPrefix(path, "/api") && !strings.HasPrefix(route.ControllerName, "Api") { if strings.HasPrefix(path, "/api") && !strings.HasPrefix(route.ControllerName, "App\\api") {
route.ControllerName = "Api" + route.ControllerName route.ControllerName = "App\\api" + strings.Split(route.ControllerName, "\\")[1]
} else if strings.HasPrefix(path, "/member") && !strings.HasPrefix(route.ControllerName, "Member") { // route.ControllerName = "App\\apifile"
} else if strings.HasPrefix(path, "/member") && !strings.HasPrefix(route.ControllerName, "App\\member") {
// member设置 // member设置
route.ControllerName = "Member" + route.ControllerName // route.ControllerName = "App\\Member" + route.ControllerName
route.ControllerName = "App\\member" + strings.Split(route.ControllerName, "\\")[1]
} }
// end // end
} }
@@ -78,7 +91,7 @@ func RouterFilter(c *revel.Controller, fc []revel.Filter) {
arg := c.MethodType.Args[i] arg := c.MethodType.Args[i]
c.Params.Fixed.Set(arg.Name, value) c.Params.Fixed.Set(arg.Name, value)
} else { } else {
revel.WARN.Println("Too many parameters to", route.Action, "trying to add", value) // revel.WARN.Println("Too many parameters to", route.Action, "trying to add", value)
break break
} }
} }

View File

@@ -1 +0,0 @@
Compress and combine js files

View File

@@ -1,159 +0,0 @@
package main
import (
"fmt"
"io/ioutil"
"os"
"os/exec"
"strings"
// "time"
)
/*
用golang exec 总是说找不到uglifyjs命令, 需要全部路径
而且node, npm要在/usr/bin下, 已建ln
*/
/*
<script src="js/jquery-1.9.0.min.js"></script>
<!-- 以后将所有的js压缩合并成一个文件 -->
<script src="js/jquery-cookie.js"></script>
<script src="js/bootstrap.js"></script>
<script type="text/javascript" src="tinymce/tinymce.js"></script>
<script src="js/common.js"></script>
<script src="js/app/note.js"></script>
<script src="js/app/tag.js"></script>
<script src="js/app/notebook.js"></script>
<script src="js/app/share.js"></script>
<script src="js/object_id.js"></script>
<script type="text/javascript" src="js/ZeroClipboard/ZeroClipboard.js"></script>
*/
//var jss = []string{"js/jquery-cookie", "js/bootstrap"}
var jss = []string{"js/jquery-cookie", "js/bootstrap",
"js/common", "js/app/note", "js/app/tag", "js/app/notebook", "js/app/share",
"js/object_id", "js/ZeroClipboard/ZeroClipboard"}
var base1 = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/"
var base = "/Users/life/Documents/Go/package2/src/github.com/leanote/leanote/public/"
var cmdPath = "/usr/local/bin/uglifyjs"
func cmdError(err error) {
if err != nil {
fmt.Println(err)
fmt.Fprintf(os.Stderr, "The command failed to perform: %s (Command: %s, Arguments: %s)", err, "", "")
} else {
fmt.Println("OK")
}
}
// filename没有扩展名
func compressJs(filename string) {
source := base + filename + ".js"
to := base + filename + "-min.js"
cmd := exec.Command(cmdPath, source, "-o", to)
_, err := cmd.CombinedOutput()
fmt.Println(source)
cmdError(err)
}
func combineJs() {
// 生成一个总文件
cmd := exec.Command("rm", base+"js/all.js")
_, err := cmd.CombinedOutput()
cmdError(err)
for _, js := range jss {
to := base + js + "-min.js"
fmt.Println(to)
compressJs(js)
// 每个压缩后的文件放入之
cmd2 := exec.Command("/bin/sh", "-c", "cat "+to+" >> "+base+"js/all.js")
_, err := cmd2.CombinedOutput()
cmdError(err)
cmd2 = exec.Command("/bin/sh", "-c", "cat \n >> "+base+"js/all.js")
_, err = cmd2.CombinedOutput()
cmdError(err)
}
}
// 改note-dev->note
func dev() {
// 即替换note.js->note-min.js
m := map[string]string{"tinymce.dev.js": "tinymce.min.js",
"tinymce.js": "tinymce.min.js",
"jquery.ztree.all-3.5.js": "jquery.ztree.all-3.5-min.js",
"note.js": "note-min.js",
"app.js": "app-min.js",
"page.js": "page-min.js",
"common.js": "common-min.js",
"notebook.js": "notebook-min.js",
"share.js": "share-min.js",
"tag.js": "tag-min.js",
"jquery.slimscroll.js": "jquery.slimscroll-min.js",
"jquery.contextmenu.js": "jquery.contextmenu-min.js",
"editor/editor.js": "editor/editor-min.js",
"/public/mdeditor/editor/scrollLink.js": "/public/mdeditor/editor/scrollLink-min.js",
"console.log(o);": "",
}
path := base1 + "/src/views/note/note-dev.html"
target := base1 + "/src/views/note/note.html"
bs, _ := ioutil.ReadFile(path)
content := string(bs)
print(content)
for key, value := range m {
content = strings.Replace(content, key, value, -1)
}
// var time = time.Now().Unix() % 1000
// content = strings.Replace(content, "-min.js", fmt.Sprintf("-min.js?r=%d", time), -1)
// content = strings.Replace(content, "default{{end}}.css", fmt.Sprintf("default{{end}}.css?r=%d", time), 1)
// content = strings.Replace(content, "writting-overwrite.css", fmt.Sprintf("writting-overwrite.css?r=%d", time), 1)
ioutil.WriteFile(target, []byte(content), os.ModeAppend)
}
// 压缩js成一块
func tinymce() {
// cmdStr := "node_modules/jake/bin/cli.js minify bundle[themes:modern,plugins:table,paste,advlist,autolink,link,image,lists,charmap,hr,searchreplace,visualblocks,visualchars,code,nav,tabfocus,contextmenu,directionality,codemirror,codesyntax,textcolor,fullpage]"
// cmd := exec.Command("/Users/life/Documents/eclipse-workspace/go/leanote_release/tinymce-master/node_modules/jake/bin/cli.js", "minify", "bundle[themes:modern,plugins:table,paste,advlist,autolink,link,image,lists,charmap,hr,searchreplace,visualblocks,visualchars,code,nav,tabfocus,contextmenu,directionality,codemirror,codesyntax,textcolor,fullpage]")
cmd := exec.Command("/bin/sh", "-c", "grunt minify")
cmd.Dir = base + "/tinymce_4.1.9"
fmt.Println("正在build tinymce")
// 必须要先删除
cmd2 := exec.Command("/bin/sh", "-c", "rm "+cmd.Dir+"/js/tinymce/tinymce.dev.js")
cmd2.CombinedOutput()
cmd2 = exec.Command("/bin/sh", "-c", "rm "+cmd.Dir+"/js/tinymce/tinymce.jquery.dev.js")
c, _ := cmd2.CombinedOutput()
fmt.Println(string(c))
c, _ = cmd.CombinedOutput()
fmt.Println(string(c))
}
func main() {
// 压缩tinymce
// tinymce()
dev()
// 其它零散的需要压缩的js
otherJss := []string{"js/main", "js/app/page", "js/contextmenu/jquery.contextmenu",
"js/jquery.ztree.all-3.5",
"js/jQuery-slimScroll-1.3.0/jquery.slimscroll",
}
for _, js := range otherJss {
compressJs(js)
}
// 先压缩后合并
combineJs()
}

View File

@@ -606,5 +606,5 @@ func (this *ConfigService) HomePageIsAdminsBlog() bool {
} }
func (this *ConfigService) GetVersion() string { func (this *ConfigService) GetVersion() string {
return "2.4" return "2.6.1"
} }

View File

@@ -616,7 +616,7 @@ func (this *NoteService) UpdateNoteContent(updatedUserId, noteId, content, abstr
return false, "conflict", 0 return false, "conflict", 0
} }
afterUsn = userService.IncrUsn(userId) afterUsn = userService.IncrUsn(userId)
db.UpdateByIdAndUserIdField(db.Notes, noteId, userId, "Usn", usn) db.UpdateByIdAndUserIdField(db.Notes, noteId, userId, "Usn", afterUsn)
} }
if db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, data) { if db.UpdateByIdAndUserIdMap(db.NoteContents, noteId, userId, data) {
@@ -1106,7 +1106,7 @@ func (this *NoteService) FixContent(content string, isMarkdown bool) string {
reg2, _ = regexp.Compile("<a(?:[^>]+?)(" + eachPattern["src"] + `=['"]*` + baseUrlPattern + eachPattern["middle"] + `\?` + eachPattern["param"] + `=([a-z0-9A-Z]{24})["']*)[^>]*>`) reg2, _ = regexp.Compile("<a(?:[^>]+?)(" + eachPattern["src"] + `=['"]*` + baseUrlPattern + eachPattern["middle"] + `\?` + eachPattern["param"] + `=([a-z0-9A-Z]{24})["']*)[^>]*>`)
} }
Log(reg2) // Log(reg2)
content = reg.ReplaceAllStringFunc(content, func(str string) string { content = reg.ReplaceAllStringFunc(content, func(str string) string {
// str=这样的 // str=这样的

View File

@@ -360,8 +360,8 @@ func (this *NotebookService) DragNotebooks(userId string, curNotebookId string,
func (this *NotebookService) ReCountNotebookNumberNotes(notebookId string) bool { func (this *NotebookService) ReCountNotebookNumberNotes(notebookId string) bool {
notebookIdO := bson.ObjectIdHex(notebookId) notebookIdO := bson.ObjectIdHex(notebookId)
count := db.Count(db.Notes, bson.M{"NotebookId": notebookIdO, "IsTrash": false, "IsDeleted": false}) count := db.Count(db.Notes, bson.M{"NotebookId": notebookIdO, "IsTrash": false, "IsDeleted": false})
Log(count) // Log(count)
Log(notebookId) // Log(notebookId)
return db.UpdateByQField(db.Notebooks, bson.M{"_id": notebookIdO}, "NumberNotes", count) return db.UpdateByQField(db.Notebooks, bson.M{"_id": notebookIdO}, "NumberNotes", count)
} }

View File

@@ -3,7 +3,7 @@ package service
import ( import (
"github.com/leanote/leanote/app/db" "github.com/leanote/leanote/app/db"
"github.com/leanote/leanote/app/info" "github.com/leanote/leanote/app/info"
. "github.com/leanote/leanote/app/lea" // . "github.com/leanote/leanote/app/lea"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
"time" "time"
// "strings" // "strings"
@@ -66,10 +66,10 @@ func (this *SessionService) GetCaptcha(sessionId string) string {
} }
func (this *SessionService) SetCaptcha(sessionId, captcha string) bool { func (this *SessionService) SetCaptcha(sessionId, captcha string) bool {
this.Get(sessionId) this.Get(sessionId)
Log(sessionId) // Log(sessionId)
Log(captcha) // Log(captcha)
ok := this.Update(sessionId, "Captcha", captcha) ok := this.Update(sessionId, "Captcha", captcha)
Log(ok) // Log(ok)
return ok return ok
} }

View File

@@ -311,7 +311,7 @@ func (this *ThemeService) UpdateTplContent(userId, themeId, filename, content st
basePath := this.GetThemeAbsolutePath(userId, themeId) basePath := this.GetThemeAbsolutePath(userId, themeId)
path := basePath + "/" + filename path := basePath + "/" + filename
if strings.Contains(filename, ".html") { if strings.Contains(filename, ".html") {
Log(">>") // Log(">>")
if ok, msg = this.ValidateTheme(basePath, filename, content); ok { if ok, msg = this.ValidateTheme(basePath, filename, content); ok {
// 模板 // 模板
if ok, msg = this.mustTpl(filename, content); ok { if ok, msg = this.mustTpl(filename, content); ok {
@@ -359,7 +359,7 @@ func (this *ThemeService) mustTpl(filename, content string) (ok bool, msg string
defer func() { defer func() {
if err := recover(); err != nil { if err := recover(); err != nil {
ok = false ok = false
Log(err) // Log(err)
msg = fmt.Sprintf("%v", err) msg = fmt.Sprintf("%v", err)
} }
}() }()
@@ -405,7 +405,7 @@ func (this *ThemeService) ExportTheme(userId, themeId string) (ok bool, path str
theme := this.GetThemeById(themeId) theme := this.GetThemeById(themeId)
// 打包 // 打包
// 验证路径, 别把整个项目打包了 // 验证路径, 别把整个项目打包了
Log(theme.Path) // Log(theme.Path)
if theme.Path == "" || if theme.Path == "" ||
(!strings.HasPrefix(theme.Path, "public/upload") && (!strings.HasPrefix(theme.Path, "public/upload") &&
!strings.HasPrefix(theme.Path, "public/blog/themes")) || !strings.HasPrefix(theme.Path, "public/blog/themes")) ||
@@ -417,12 +417,12 @@ func (this *ThemeService) ExportTheme(userId, themeId string) (ok bool, path str
targetPath := revel.BasePath + "/public/upload/" + userId + "/tmp" targetPath := revel.BasePath + "/public/upload/" + userId + "/tmp"
err := os.MkdirAll(targetPath, 0755) err := os.MkdirAll(targetPath, 0755)
if err != nil { if err != nil {
Log(err) // Log(err)
return return
} }
targetName := targetPath + "/" + theme.Name + ".zip" targetName := targetPath + "/" + theme.Name + ".zip"
Log(sourcePath) // Log(sourcePath)
Log(targetName) // Log(targetName)
ok = archive.Zip(sourcePath, targetName) ok = archive.Zip(sourcePath, targetName)
if !ok { if !ok {
return return
@@ -445,7 +445,7 @@ func (this *ThemeService) ImportTheme(userId, path string) (ok bool, msg string)
} }
if ok, msg = archive.Unzip(path, targetPath); !ok { if ok, msg = archive.Unzip(path, targetPath); !ok {
DeleteFile(targetPath) DeleteFile(targetPath)
Log("oh no") // Log("oh no")
return return
} }
@@ -558,8 +558,8 @@ func (this *ThemeService) InstallTheme(userId, themeId string) (ok bool) {
// 验证主题是否全法, 存在循环引用? // 验证主题是否全法, 存在循环引用?
// filename, newContent 表示在修改模板时要判断模板修改时是否有错误 // filename, newContent 表示在修改模板时要判断模板修改时是否有错误
func (this *ThemeService) ValidateTheme(path string, filename, newContent string) (ok bool, msg string) { func (this *ThemeService) ValidateTheme(path string, filename, newContent string) (ok bool, msg string) {
Log("theme Path") // Log("theme Path")
Log(path) // Log(path)
// 建立一个有向图 // 建立一个有向图
// 将该path下的所有文件提出, 得到文件的引用情况 // 将该path下的所有文件提出, 得到文件的引用情况
files := ListDir(path) files := ListDir(path)
@@ -613,9 +613,9 @@ func (this *ThemeService) ValidateTheme(path string, filename, newContent string
for _, includes := range finds { for _, includes := range finds {
include := includes[1] include := includes[1]
includeIndex, has := fileIndexMap[include] includeIndex, has := fileIndexMap[include]
Log(includeIndex) // Log(includeIndex)
Log("??") // Log("??")
Log(has) // Log(has)
if has { if has {
vector[thisIndex][includeIndex] = 1 vector[thisIndex][includeIndex] = 1
} }

View File

@@ -485,7 +485,7 @@ func (this *UserService) GetAllUserByFilter(userFilterEmail, userFilterWhiteList
users := []info.User{} users := []info.User{}
q := db.Users.Find(query) q := db.Users.Find(query)
q.All(&users) q.All(&users)
Log(len(users)) // Log(len(users))
return users return users
} }

View File

@@ -3,16 +3,16 @@
<div class="row"> <div class="row">
<div class="col-sm-6"> <div class="col-sm-8">
<form id="add_user_form"> <form id="add_user_form">
<section class="panel panel-default"> <section class="panel panel-default">
<div class="panel-body"> <div class="panel-body">
<div class="form-group"> <div class="form-group">
<label>Wkhtmltopdf Binary Path</label> <label>Wkhtmltopdf Binary Path</label>
<input type="text" class="form-control" placeholder="/usr/local/bin/wkhtmltopdf" name="path" value="{{.str.exportPdfBinPath}}"> <input type="text" class="form-control" placeholder="/usr/local/bin/wkhtmltopdf or C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe" name="path" value="{{.str.exportPdfBinPath}}">
Leanote use <a target="_blank" href="http://wkhtmltopdf.org">wkhtmltopdf</a> to export pdf. You should install it on your server. Leanote use <a target="_blank" href="http://wkhtmltopdf.org">wkhtmltopdf</a> to export pdf. You should install it on your server.
<br /> <br />
Please input the path that wkhtmltopdf installed, e.g. <code>/usr/local/bin/wkhtmltopdf</code> Please input the path that wkhtmltopdf installed, e.g. <code>/usr/local/bin/wkhtmltopdf</code> on mac os/linux or <code>C:\Program Files\wkhtmltopdf\bin\wkhtmltopdf.exe</code> on windows.
</div> </div>
</div> </div>

View File

@@ -1,152 +0,0 @@
#!/bin/sh
# release leanote
# 当前路径
SP=$(cd "$(dirname "$0")"; pwd)
# tmp path to store leanote release files
tmp="/Users/life/Desktop/leanote_release"
# version
V="v2.4"
##=================================
# 1. 先build 成 3个平台, 2种bit = 6种
##=================================
# cd /Documents/Go/package2/src/github.com/leanote/leanote/bin
# GOOS=darwin GOARCH=amd64 go build -o leanote-darwin-amd64 ../app/tmp
cd $SP
# $1 = darwin, linux
# $2 = amd64
function build()
{
echo build-$1-$2
if [ $1 = "linux" -o $1 = "darwin" ]
then
suffix=""
else
suffix=".exe"
fi
GOOS=$1 GOARCH=$2 go build -o leanote-$1-$2$suffix ../app/tmp
}
build "linux" "386";
build "linux" "amd64";
build "linux" "arm";
build "windows" "386";
build "windows" "amd64";
# build "darwin" "386";
build "darwin" "amd64";
##======================
# 2. release目录准备工作
##======================
rm -rf $tmp/leanote
mkdir -p $tmp/leanote/app
mkdir -p $tmp/leanote/conf
mkdir -p $tmp/leanote/bin
##==================
# 3. 复制
##==================
cd $SP
cd ../
# bin
cp -r ./bin/src $tmp/leanote/bin/
cp ./bin/run.sh $tmp/leanote/bin/
cp ./bin/run.bat $tmp/leanote/bin/
# views
cp -r ./app/views $tmp/leanote/app
# 可不要, 源码
#cp -r ./app/service $tmp/leanote/app/service
#cp -r ./app/controllers $tmp/leanote/app/controllers
#cp -r ./app/db $tmp/leanote/app/db
#cp -r ./app/info $tmp/leanote/app/info
#cp -r ./app/lea $tmp/leanote/app/lea
# conf
cp ./conf/app.conf $tmp/leanote/conf/app.conf
cp ./conf/routes $tmp/leanote/conf/routes
# 处理app.conf, 还原配置
cat $tmp/leanote/conf/app.conf | sed 's/db.dbname=leanote.*#/db.dbname=leanote #/' > $tmp/leanote/conf/app.conf2 # 不能直接覆盖
rm $tmp/leanote/conf/app.conf
mv $tmp/leanote/conf/app.conf2 $tmp/leanote/conf/app.conf
# others
cp -r ./messages ./public ./mongodb_backup $tmp/leanote/
# delete some files
rm -r $tmp/leanote/public/tinymce/classes
rm -r $tmp/leanote/public/upload
mkdir $tmp/leanote/public/upload
rm -r $tmp/leanote/public/.codekit-cache
rm $tmp/leanote/public/.DS_Store
rm $tmp/leanote/public/config.codekit
# make link
# cd $tmp/leanote/bin
# ln -s ../../../../ ./src/github.com/leanote/leanote
# archieve
# << 'BLOCK
##===========
# 4. 打包
##===========
# $1 = linux
# $2 = 386, amd64
# 创建一个$V的目录存放之
rm -rf $tmp/$V
mkdir $tmp/$V
function tarRelease()
{
echo tar-$1-$2
cd $SP
cd ../
rm $tmp/leanote/bin/leanote-* # 删除之前的bin文件
rm $tmp/leanote/bin/run* # 删除之前的run.sh 或 run.bat
if [ $1 = "linux" -o $1 = "darwin" ]
then
suffix=""
if [ $2 = "arm" ]
then
cp ./bin/run-arm.sh $tmp/leanote/bin/run.sh
else
cp ./bin/run-$1-$2.sh $tmp/leanote/bin/run.sh
fi
else
cp ./bin/run.bat $tmp/leanote/bin/
suffix=".exe"
fi
cp ./bin/leanote-$1-$2$suffix $tmp/leanote/bin/
cd $tmp
tar -cf $tmp/$V/leanote-$1-$2-$V.bin.tar leanote
gzip $tmp/$V/leanote-$1-$2-$V.bin.tar
}
tarRelease "linux" "386";
tarRelease "linux" "amd64";
tarRelease "linux" "arm";
tarRelease "windows" "386";
tarRelease "windows" "amd64";
# tarRelease "darwin" "386";
tarRelease "darwin" "amd64";
# BLOCK'

View File

@@ -1,18 +0,0 @@
#!/bin/sh
SCRIPTPATH=$(cd "$(dirname "$0")"; pwd)
# set link
path="$SCRIPTPATH/src/github.com/leanote"
if [ ! -d "$path" ]; then
mkdir -p "$path"
fi
rm -rf $SCRIPTPATH/src/github.com/leanote/leanote # 先删除
ln -s ../../../../ $SCRIPTPATH/src/github.com/leanote/leanote
# set GOPATH
export GOPATH=$SCRIPTPATH
script="$SCRIPTPATH/leanote-linux-arm"
chmod 777 $script
$script -importPath github.com/leanote/leanote

View File

@@ -1,18 +0,0 @@
#!/bin/sh
SCRIPTPATH=$(cd "$(dirname "$0")"; pwd)
# set link
path="$SCRIPTPATH/src/github.com/leanote"
if [ ! -d "$path" ]; then
mkdir -p "$path"
fi
rm -rf $SCRIPTPATH/src/github.com/leanote/leanote # 先删除
ln -s ../../../../ $SCRIPTPATH/src/github.com/leanote/leanote
# set GOPATH
export GOPATH=$SCRIPTPATH
script="$SCRIPTPATH/leanote-darwin-amd64"
chmod 777 $script
$script -importPath github.com/leanote/leanote

View File

@@ -1,18 +0,0 @@
#!/bin/sh
SCRIPTPATH=$(cd "$(dirname "$0")"; pwd)
# set link
path="$SCRIPTPATH/src/github.com/leanote"
if [ ! -d "$path" ]; then
mkdir -p "$path"
fi
rm -rf $SCRIPTPATH/src/github.com/leanote/leanote # 先删除
ln -s ../../../../ $SCRIPTPATH/src/github.com/leanote/leanote
# set GOPATH
export GOPATH=$SCRIPTPATH
script="$SCRIPTPATH/leanote-linux-386"
chmod 777 $script
$script -importPath github.com/leanote/leanote

View File

@@ -1,18 +0,0 @@
#!/bin/sh
SCRIPTPATH=$(cd "$(dirname "$0")"; pwd)
# set link
path="$SCRIPTPATH/src/github.com/leanote"
if [ ! -d "$path" ]; then
mkdir -p "$path"
fi
rm -rf $SCRIPTPATH/src/github.com/leanote/leanote # 先删除
ln -s ../../../../ $SCRIPTPATH/src/github.com/leanote/leanote
# set GOPATH
export GOPATH=$SCRIPTPATH
script="$SCRIPTPATH/leanote-linux-amd64"
chmod 777 $script
$script -importPath github.com/leanote/leanote

View File

@@ -1,23 +0,0 @@
@echo off
cd..
set SCRIPTPATH=%cd%
: top src directory
set path="%SCRIPTPATH%\bin\src\github.com\leanote"
if not exist "%path%" mkdir "%path%"
: create software link
if exist "%path%\leanote" del /Q "%path%\leanote"
mklink /D "%path%\leanote" %SCRIPTPATH%
: set GOPATH
set GOPATH="%SCRIPTPATH%\bin"
: run
if %processor_architecture%==x86 (
"%SCRIPTPATH%\bin\leanote-windows-386.exe" -importPath github.com/leanote/leanote
) else (
"%SCRIPTPATH%\bin\leanote-windows-amd64.exe" -importPath github.com/leanote/leanote
)

View File

@@ -1,545 +0,0 @@
3dm=x-world/x-3dmf
3dmf=x-world/x-3dmf
7z=application/x-7z-compressed
a=application/octet-stream
aab=application/x-authorware-bin
aam=application/x-authorware-map
aas=application/x-authorware-seg
abc=text/vndabc
ace=application/x-ace-compressed
acgi=text/html
afl=video/animaflex
ai=application/postscript
aif=audio/aiff
aifc=audio/aiff
aiff=audio/aiff
aim=application/x-aim
aip=text/x-audiosoft-intra
alz=application/x-alz-compressed
ani=application/x-navi-animation
aos=application/x-nokia-9000-communicator-add-on-software
aps=application/mime
arc=application/x-arc-compressed
arj=application/arj
art=image/x-jg
asf=video/x-ms-asf
asm=text/x-asm
asp=text/asp
asx=application/x-mplayer2
au=audio/basic
avi=video/x-msvideo
avs=video/avs-video
bcpio=application/x-bcpio
bin=application/mac-binary
bmp=image/bmp
boo=application/book
book=application/book
boz=application/x-bzip2
bsh=application/x-bsh
bz2=application/x-bzip2
bz=application/x-bzip
c++=text/plain
c=text/x-c
cab=application/vnd.ms-cab-compressed
cat=application/vndms-pkiseccat
cc=text/x-c
ccad=application/clariscad
cco=application/x-cocoa
cdf=application/cdf
cer=application/pkix-cert
cha=application/x-chat
chat=application/x-chat
chrt=application/vnd.kde.kchart
class=application/java
# ? class=application/java-vm
com=text/plain
conf=text/plain
cpio=application/x-cpio
cpp=text/x-c
cpt=application/mac-compactpro
crl=application/pkcs-crl
crt=application/pkix-cert
crx=application/x-chrome-extension
csh=text/x-scriptcsh
css=text/css
csv=text/csv
cxx=text/plain
dar=application/x-dar
dcr=application/x-director
deb=application/x-debian-package
deepv=application/x-deepv
def=text/plain
der=application/x-x509-ca-cert
dif=video/x-dv
dir=application/x-director
divx=video/divx
dl=video/dl
dmg=application/x-apple-diskimage
doc=application/msword
dot=application/msword
dp=application/commonground
drw=application/drafting
dump=application/octet-stream
dv=video/x-dv
dvi=application/x-dvi
dwf=drawing/x-dwf=(old)
dwg=application/acad
dxf=application/dxf
dxr=application/x-director
el=text/x-scriptelisp
elc=application/x-bytecodeelisp=(compiled=elisp)
eml=message/rfc822
env=application/x-envoy
eps=application/postscript
es=application/x-esrehber
etx=text/x-setext
evy=application/envoy
exe=application/octet-stream
f77=text/x-fortran
f90=text/x-fortran
f=text/x-fortran
fdf=application/vndfdf
fif=application/fractals
fli=video/fli
flo=image/florian
flv=video/x-flv
flx=text/vndfmiflexstor
fmf=video/x-atomic3d-feature
for=text/x-fortran
fpx=image/vndfpx
frl=application/freeloader
funk=audio/make
g3=image/g3fax
g=text/plain
gif=image/gif
gl=video/gl
gsd=audio/x-gsm
gsm=audio/x-gsm
gsp=application/x-gsp
gss=application/x-gss
gtar=application/x-gtar
gz=application/x-compressed
gzip=application/x-gzip
h=text/x-h
hdf=application/x-hdf
help=application/x-helpfile
hgl=application/vndhp-hpgl
hh=text/x-h
hlb=text/x-script
hlp=application/hlp
hpg=application/vndhp-hpgl
hpgl=application/vndhp-hpgl
hqx=application/binhex
hta=application/hta
htc=text/x-component
htm=text/html
html=text/html
htmls=text/html
htt=text/webviewhtml
htx=text/html
ice=x-conference/x-cooltalk
ico=image/x-icon
ics=text/calendar
icz=text/calendar
idc=text/plain
ief=image/ief
iefs=image/ief
iges=application/iges
igs=application/iges
ima=application/x-ima
imap=application/x-httpd-imap
inf=application/inf
ins=application/x-internett-signup
ip=application/x-ip2
isu=video/x-isvideo
it=audio/it
iv=application/x-inventor
ivr=i-world/i-vrml
ivy=application/x-livescreen
jam=audio/x-jam
jav=text/x-java-source
java=text/x-java-source
jcm=application/x-java-commerce
jfif-tbnl=image/jpeg
jfif=image/jpeg
jnlp=application/x-java-jnlp-file
jpe=image/jpeg
jpeg=image/jpeg
jpg=image/jpeg
jps=image/x-jps
js=application/javascript
json=application/json
jut=image/jutvision
kar=audio/midi
karbon=application/vnd.kde.karbon
kfo=application/vnd.kde.kformula
flw=application/vnd.kde.kivio
kml=application/vnd.google-earth.kml+xml
kmz=application/vnd.google-earth.kmz
kon=application/vnd.kde.kontour
kpr=application/vnd.kde.kpresenter
kpt=application/vnd.kde.kpresenter
ksp=application/vnd.kde.kspread
kwd=application/vnd.kde.kword
kwt=application/vnd.kde.kword
ksh=text/x-scriptksh
la=audio/nspaudio
lam=audio/x-liveaudio
latex=application/x-latex
lha=application/lha
lhx=application/octet-stream
list=text/plain
lma=audio/nspaudio
log=text/plain
lsp=text/x-scriptlisp
lst=text/plain
lsx=text/x-la-asf
ltx=application/x-latex
lzh=application/octet-stream
lzx=application/lzx
m1v=video/mpeg
m2a=audio/mpeg
m2v=video/mpeg
m3u=audio/x-mpegurl
m=text/x-m
man=application/x-troff-man
manifest=text/cache-manifest
map=application/x-navimap
mar=text/plain
mbd=application/mbedlet
mc$=application/x-magic-cap-package-10
mcd=application/mcad
mcf=text/mcf
mcp=application/netmc
me=application/x-troff-me
mht=message/rfc822
mhtml=message/rfc822
mid=application/x-midi
midi=application/x-midi
mif=application/x-frame
mime=message/rfc822
mjf=audio/x-vndaudioexplosionmjuicemediafile
mjpg=video/x-motion-jpeg
mm=application/base64
mme=application/base64
mod=audio/mod
moov=video/quicktime
mov=video/quicktime
movie=video/x-sgi-movie
mp2=audio/mpeg
mp3=audio/mpeg3
mp4=video/mp4
mpa=audio/mpeg
mpc=application/x-project
mpe=video/mpeg
mpeg=video/mpeg
mpg=video/mpeg
mpga=audio/mpeg
mpp=application/vndms-project
mpt=application/x-project
mpv=application/x-project
mpx=application/x-project
mrc=application/marc
ms=application/x-troff-ms
mv=video/x-sgi-movie
my=audio/make
mzz=application/x-vndaudioexplosionmzz
nap=image/naplps
naplps=image/naplps
nc=application/x-netcdf
ncm=application/vndnokiaconfiguration-message
nif=image/x-niff
niff=image/x-niff
nix=application/x-mix-transfer
nsc=application/x-conference
nvd=application/x-navidoc
o=application/octet-stream
oda=application/oda
odb=application/vnd.oasis.opendocument.database
odc=application/vnd.oasis.opendocument.chart
odf=application/vnd.oasis.opendocument.formula
odg=application/vnd.oasis.opendocument.graphics
odi=application/vnd.oasis.opendocument.image
odm=application/vnd.oasis.opendocument.text-master
odp=application/vnd.oasis.opendocument.presentation
ods=application/vnd.oasis.opendocument.spreadsheet
odt=application/vnd.oasis.opendocument.text
oga=audio/ogg
ogg=audio/ogg
ogv=video/ogg
omc=application/x-omc
omcd=application/x-omcdatamaker
omcr=application/x-omcregerator
otc=application/vnd.oasis.opendocument.chart-template
otf=application/vnd.oasis.opendocument.formula-template
otg=application/vnd.oasis.opendocument.graphics-template
oth=application/vnd.oasis.opendocument.text-web
oti=application/vnd.oasis.opendocument.image-template
otm=application/vnd.oasis.opendocument.text-master
otp=application/vnd.oasis.opendocument.presentation-template
ots=application/vnd.oasis.opendocument.spreadsheet-template
ott=application/vnd.oasis.opendocument.text-template
p10=application/pkcs10
p12=application/pkcs-12
p7a=application/x-pkcs7-signature
p7c=application/pkcs7-mime
p7m=application/pkcs7-mime
p7r=application/x-pkcs7-certreqresp
p7s=application/pkcs7-signature
p=text/x-pascal
part=application/pro_eng
pas=text/pascal
pbm=image/x-portable-bitmap
pcl=application/vndhp-pcl
pct=image/x-pict
pcx=image/x-pcx
pdb=chemical/x-pdb
pdf=application/pdf
pfunk=audio/make
pgm=image/x-portable-graymap
pic=image/pict
pict=image/pict
pkg=application/x-newton-compatible-pkg
pko=application/vndms-pkipko
pl=text/x-scriptperl
plx=application/x-pixclscript
pm4=application/x-pagemaker
pm5=application/x-pagemaker
pm=text/x-scriptperl-module
png=image/png
pnm=application/x-portable-anymap
pot=application/mspowerpoint
pov=model/x-pov
ppa=application/vndms-powerpoint
ppm=image/x-portable-pixmap
pps=application/mspowerpoint
ppt=application/mspowerpoint
ppz=application/mspowerpoint
pre=application/x-freelance
prt=application/pro_eng
ps=application/postscript
psd=application/octet-stream
pvu=paleovu/x-pv
pwz=application/vndms-powerpoint
py=text/x-scriptphyton
pyc=applicaiton/x-bytecodepython
qcp=audio/vndqcelp
qd3=x-world/x-3dmf
qd3d=x-world/x-3dmf
qif=image/x-quicktime
qt=video/quicktime
qtc=video/x-qtc
qti=image/x-quicktime
qtif=image/x-quicktime
ra=audio/x-pn-realaudio
ram=audio/x-pn-realaudio
rar=application/x-rar-compressed
ras=application/x-cmu-raster
rast=image/cmu-raster
rexx=text/x-scriptrexx
rf=image/vndrn-realflash
rgb=image/x-rgb
rm=application/vndrn-realmedia
rmi=audio/mid
rmm=audio/x-pn-realaudio
rmp=audio/x-pn-realaudio
rng=application/ringing-tones
rnx=application/vndrn-realplayer
roff=application/x-troff
rp=image/vndrn-realpix
rpm=audio/x-pn-realaudio-plugin
rt=text/vndrn-realtext
rtf=text/richtext
rtx=text/richtext
rv=video/vndrn-realvideo
s=text/x-asm
s3m=audio/s3m
s7z=application/x-7z-compressed
saveme=application/octet-stream
sbk=application/x-tbook
scm=text/x-scriptscheme
sdml=text/plain
sdp=application/sdp
sdr=application/sounder
sea=application/sea
set=application/set
sgm=text/x-sgml
sgml=text/x-sgml
sh=text/x-scriptsh
shar=application/x-bsh
shtml=text/x-server-parsed-html
sid=audio/x-psid
skd=application/x-koan
skm=application/x-koan
skp=application/x-koan
skt=application/x-koan
sit=application/x-stuffit
sitx=application/x-stuffitx
sl=application/x-seelogo
smi=application/smil
smil=application/smil
snd=audio/basic
sol=application/solids
spc=text/x-speech
spl=application/futuresplash
spr=application/x-sprite
sprite=application/x-sprite
spx=audio/ogg
src=application/x-wais-source
ssi=text/x-server-parsed-html
ssm=application/streamingmedia
sst=application/vndms-pkicertstore
step=application/step
stl=application/sla
stp=application/step
sv4cpio=application/x-sv4cpio
sv4crc=application/x-sv4crc
svf=image/vnddwg
svg=image/svg+xml
svr=application/x-world
swf=application/x-shockwave-flash
t=application/x-troff
talk=text/x-speech
tar=application/x-tar
tbk=application/toolbook
tcl=text/x-scripttcl
tcsh=text/x-scripttcsh
tex=application/x-tex
texi=application/x-texinfo
texinfo=application/x-texinfo
text=text/plain
tgz=application/gnutar
tif=image/tiff
tiff=image/tiff
tr=application/x-troff
tsi=audio/tsp-audio
tsp=application/dsptype
tsv=text/tab-separated-values
turbot=image/florian
txt=text/plain
uil=text/x-uil
uni=text/uri-list
unis=text/uri-list
unv=application/i-deas
uri=text/uri-list
uris=text/uri-list
ustar=application/x-ustar
uu=text/x-uuencode
uue=text/x-uuencode
vcd=application/x-cdlink
vcf=text/x-vcard
vcard=text/x-vcard
vcs=text/x-vcalendar
vda=application/vda
vdo=video/vdo
vew=application/groupwise
viv=video/vivo
vivo=video/vivo
vmd=application/vocaltec-media-desc
vmf=application/vocaltec-media-file
voc=audio/voc
vos=video/vosaic
vox=audio/voxware
vqe=audio/x-twinvq-plugin
vqf=audio/x-twinvq
vql=audio/x-twinvq-plugin
vrml=application/x-vrml
vrt=x-world/x-vrt
vsd=application/x-visio
vst=application/x-visio
vsw=application/x-visio
w60=application/wordperfect60
w61=application/wordperfect61
w6w=application/msword
wav=audio/wav
wb1=application/x-qpro
wbmp=image/vnd.wap.wbmp
web=application/vndxara
wiz=application/msword
wk1=application/x-123
wmf=windows/metafile
wml=text/vnd.wap.wml
wmlc=application/vnd.wap.wmlc
wmls=text/vnd.wap.wmlscript
wmlsc=application/vnd.wap.wmlscriptc
word=application/msword
wp5=application/wordperfect
wp6=application/wordperfect
wp=application/wordperfect
wpd=application/wordperfect
wq1=application/x-lotus
wri=application/mswrite
wrl=application/x-world
wrz=model/vrml
wsc=text/scriplet
wsrc=application/x-wais-source
wtk=application/x-wintalk
x-png=image/png
xbm=image/x-xbitmap
xdr=video/x-amt-demorun
xgz=xgl/drawing
xif=image/vndxiff
xl=application/excel
xla=application/excel
xlb=application/excel
xlc=application/excel
xld=application/excel
xlk=application/excel
xll=application/excel
xlm=application/excel
xls=application/excel
xlt=application/excel
xlv=application/excel
xlw=application/excel
xm=audio/xm
xml=text/xml
xmz=xgl/movie
xpix=application/x-vndls-xpix
xpm=image/x-xpixmap
xsr=video/x-amt-showrun
xwd=image/x-xwd
xyz=chemical/x-pdb
z=application/x-compress
zip=application/zip
zoo=application/octet-stream
zsh=text/x-scriptzsh
# Office 2007 mess - http://wdg.uncc.edu/Microsoft_Office_2007_MIME_Types_for_Apache_and_IIS
docx=application/vnd.openxmlformats-officedocument.wordprocessingml.document
docm=application/vnd.ms-word.document.macroEnabled.12
dotx=application/vnd.openxmlformats-officedocument.wordprocessingml.template
dotm=application/vnd.ms-word.template.macroEnabled.12
xlsx=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsm=application/vnd.ms-excel.sheet.macroEnabled.12
xltx=application/vnd.openxmlformats-officedocument.spreadsheetml.template
xltm=application/vnd.ms-excel.template.macroEnabled.12
xlsb=application/vnd.ms-excel.sheet.binary.macroEnabled.12
xlam=application/vnd.ms-excel.addin.macroEnabled.12
pptx=application/vnd.openxmlformats-officedocument.presentationml.presentation
pptm=application/vnd.ms-powerpoint.presentation.macroEnabled.12
ppsx=application/vnd.openxmlformats-officedocument.presentationml.slideshow
ppsm=application/vnd.ms-powerpoint.slideshow.macroEnabled.12
potx=application/vnd.openxmlformats-officedocument.presentationml.template
potm=application/vnd.ms-powerpoint.template.macroEnabled.12
ppam=application/vnd.ms-powerpoint.addin.macroEnabled.12
sldx=application/vnd.openxmlformats-officedocument.presentationml.slide
sldm=application/vnd.ms-powerpoint.slide.macroEnabled.12
thmx=application/vnd.ms-officetheme
onetoc=application/onenote
onetoc2=application/onenote
onetmp=application/onenote
onepkg=application/onenote
# koffice
# iWork
key=application/x-iwork-keynote-sffkey
kth=application/x-iwork-keynote-sffkth
nmbtemplate=application/x-iwork-numbers-sfftemplate
numbers=application/x-iwork-numbers-sffnumbers
pages=application/x-iwork-pages-sffpages
template=application/x-iwork-pages-sfftemplate
# Extensions for Mozilla apps (Firefox and friends)
xpi=application/x-xpinstall
# Opera extensions
oex=application/x-opera-extension

View File

@@ -1,16 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Forbidden</title>
</head>
<body>
{{with .Error}}
<h1>
{{.Title}}
</h1>
<p>
{{.Description}}
</p>
{{end}}
</body>
</html>

View File

@@ -1,4 +0,0 @@
{
"title": "{{js .Error.Title}}",
"description": "{{js .Error.Description}}"
}

View File

@@ -1,3 +0,0 @@
{{.Error.Title}}
{{.Error.Description}}

View File

@@ -1 +0,0 @@
<forbidden>{{.Error.Description}}</forbidden>

View File

@@ -1,63 +0,0 @@
<style type="text/css">
html, body {
margin: 0;
padding: 0;
font-family: Helvetica, Arial, Sans;
background: #EEEEEE;
}
.block {
padding: 20px;
border-bottom: 1px solid #aaa;
}
#header h1 {
font-weight: normal;
font-size: 28px;
margin: 0;
}
#more {
color: #666;
font-size: 80%;
border: none;
}
#header {
background: #FFFFCC;
}
#header p {
color: #333;
}
#routes {
background: #f6f6f6;
}
#routes h2 {
font-weight: normal;
font-size: 18px;
margin: 0 0 10px 0;
}
#routes ol {
}
#routes li {
font-size: 14px;
font-family: monospace;
color: #333;
}
</style>
<div id="header" class="block">
{{with .Error}}
<h1>
{{.Title}}
</h1>
<p>
{{.Description}}
</p>
{{end}}
</div>
<div id="routes" class="block">
<h2>These routes have been tried, in this order :</h2>
<ol>
{{range .Router.Routes}}
<li>{{pad .Method 10}}{{pad .Path 50}}{{.Action}}</li>
{{end}}
</ol>
</div>

View File

@@ -1,26 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Not found</title>
</head>
<body>
{{if .DevMode}}
{{template "errors/404-dev.html" .}}
{{else}}
{{with .Error}}
<h1>
{{.Title}}
</h1>
<p>
{{.Description}}
</p>
{{end}}
{{end}}
</body>
</html>

View File

@@ -1,4 +0,0 @@
{
"title": "{{js .Error.Title}}",
"description": "{{js .Error.Description}}"
}

View File

@@ -1,3 +0,0 @@
{{.Error.Title}}
{{.Error.Description}}

View File

@@ -1 +0,0 @@
<notfound>{{.Error.Description}}</notfound>

View File

@@ -1,118 +0,0 @@
<style type="text/css">
html, body {
margin: 0;
padding: 0;
font-family: Helvetica, Arial, Sans;
background: #EEEEEE;
}
.block {
padding: 20px;
border-bottom: 1px solid #aaa;
}
#header h1 {
font-weight: normal;
font-size: 28px;
margin: 0;
}
#more {
color: #666;
font-size: 80%;
border: none;
}
#header {
background: #fcd2da;
}
#header p {
color: #333;
}
#source {
background: #f6f6f6;
}
#source h2 {
font-weight: normal;
font-size: 18px;
margin: 0 0 10px 0;
}
#source .lineNumber {
float: left;
display: block;
width: 40px;
text-align: right;
margin-right: 10px;
font-size: 14px;
font-family: monospace;
background: #333;
color: #fff;
}
#source .line {
clear: both;
color: #333;
margin-bottom: 1px;
}
#source pre {
font-size: 14px;
margin: 0;
overflow-x: hidden;
}
#source .error {
color: #c00 !important;
}
#source .error .lineNumber {
background: #c00;
}
#source a {
text-decoration: none;
}
#source a:hover * {
cursor: pointer !important;
}
#source a:hover pre {
background: #FAFFCF !important;
}
#source em {
font-style: normal;
text-decoration: underline;
font-weight: bold;
}
#source strong {
font-style: normal;
font-weight: bold;
}
</style>
{{with .Error}}
<div id="header" class="block">
<h1>
{{.Title}}
</h1>
<p>
{{if .SourceType}}
The {{.SourceType}} <strong>{{.Path}}</strong> does not compile: <strong>{{.Description}}</strong>
{{else}}
{{.Description}}
{{end}}
</p>
</div>
{{if .Path}}
<div id="source" class="block">
<h2>In {{.Path}}
{{if .Line}}
(around {{if .Line}}line {{.Line}}{{end}}{{if .Column}} column {{.Column}}{{end}})
{{end}}
</h2>
{{range .ContextSource}}
<div class="line {{if .IsError}}error{{end}}">
<span class="lineNumber">{{.Line}}:</span>
<pre>{{.Source}}</pre>
</div>
{{end}}
</div>
{{end}}
{{if .MetaError}}
<div id="source" class="block">
<h2>Additionally, an error occurred while handling this error.</h2>
<div class="line error">
{{.MetaError}}
</div>
</div>
{{end}}
{{end}}

View File

@@ -1,16 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Application error</title>
</head>
<body>
{{if .DevMode}}
{{template "errors/500-dev.html" .}}
{{else}}
<h1>Oops, an error occured</h1>
<p>
This exception has been logged.
</p>
{{end}}
</body>
</html>

View File

@@ -1,4 +0,0 @@
{
"title": "{{js .Error.Title}}",
"description": "{{js .Error.Description}}"
}

View File

@@ -1,15 +0,0 @@
{{.Error.Title}}
{{.Error.Description}}
{{if eq .RunMode "dev"}}
{{with .Error}}
{{if .Path}}
----------
In {{.Path}} {{if .Line}}(around line {{.Line}}){{end}}
{{range .ContextSource}}
{{if .IsError}}>{{else}} {{end}} {{.Line}}: {{.Source}}{{end}}
{{end}}
{{end}}
{{end}}

View File

@@ -1,4 +0,0 @@
<error>
<title>{{.Error.Title}}</title>
<description>{{.Error.Description}}</description>
</error>

View File

@@ -1,7 +1,17 @@
# Revel build section
# This section contains values that are not reloadable
################################################################################
# Comma delimited list of folders that are included with the package, or build commands
# If you want to not include folders within these ones prefix the folder with a . to make it hidden
package.folders = conf, public, messages, mongodb_backup, app/views
#------------------------ #------------------------
# leanote config # leanote config
#------------------------ #------------------------
http.addr=0.0.0.0 # listen on all ip addresses
http.port=9000 http.port=9000
site.url=http://localhost:9000 # or http://x.com:8080, http://www.xx.com:9000 site.url=http://localhost:9000 # or http://x.com:8080, http://www.xx.com:9000
@@ -28,7 +38,6 @@ app.secret=V85ZzBeTnzpsHyjQX4zukbQ8qqtju9y2aDM55VWxAH9Qop19poekx3xkcDVvrD0y #
# for dev # for dev
#-------------------------------- #--------------------------------
app.name=leanote app.name=leanote
http.addr=
http.ssl=false http.ssl=false
cookie.httponly=false cookie.httponly=false
cookie.prefix=LEANOTE cookie.prefix=LEANOTE

View File

@@ -1,7 +1,16 @@
# Revel build section
# This section contains values that are not reloadable
################################################################################
# Comma delimited list of folders that are included with the package, or build commands
# If you want to not include folders within these ones prefix the folder with a . to make it hidden
package.folders = conf, public, messages, mongodb_backup, app/views
#------------------------ #------------------------
# leanote config # leanote config
#------------------------ #------------------------
http.addr=0.0.0.0 # listen on all ip addresses
http.port=9000 http.port=9000
site.url=http://localhost:9000 # or http://x.com:8080, http://www.xx.com:9000 site.url=http://localhost:9000 # or http://x.com:8080, http://www.xx.com:9000
@@ -29,7 +38,6 @@ app.secret=V85ZzBeTnzpsHyjQX4zukbQ8qqtju9y2aDM55VWxAH9Qop19poekx3xkcDVvrD0y
# for dev # for dev
#-------------------------------- #--------------------------------
app.name=leanote app.name=leanote
http.addr=
http.ssl=false http.ssl=false
cookie.httponly=false cookie.httponly=false
cookie.prefix=LEANOTE cookie.prefix=LEANOTE

View File

@@ -129,6 +129,14 @@ GET /upload/*filepath Static.Serve("public/upload")
* /member MemberIndex.Index * /member MemberIndex.Index
* /member/index MemberIndex.Index * /member/index MemberIndex.Index
# 不知道为什么, group就是不行 member/blog可以
* /member/group/index MemberGroup.Index
* /member/group/addGroup MemberGroup.AddGroup
* /member/group/updateGroupTitle MemberGroup.UpdateGroupTitle
* /member/group/deleteGroup MemberGroup.DeleteGroup
* /member/group/addUser MemberGroup.AddUser
* /member/group/deleteUser MemberGroup.DeleteUser
# common # common
* /:controller/:action :controller.:action * /:controller/:action :controller.:action
* /api/:controller/:action :controller.:action * /api/:controller/:action :controller.:action

31
go.mod Normal file
View File

@@ -0,0 +1,31 @@
module github.com/leanote/leanote
go 1.15
require (
github.com/PuerkitoBio/goquery v1.6.1
github.com/agtorre/gocolorize v1.0.0
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/garyburd/redigo v1.6.2 // indirect
github.com/go-stack/stack v1.8.0 // indirect
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac // indirect
github.com/jessevdk/go-flags v1.4.0
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/patrickmn/go-cache v2.1.0+incompatible // indirect
github.com/revel/cmd v1.0.3
github.com/revel/config v1.0.0 // indirect
github.com/revel/log15 v2.11.20+incompatible // indirect
github.com/revel/modules v1.0.0
github.com/revel/pathtree v0.0.0-20140121041023-41257a1839e9 // indirect
github.com/revel/revel v1.0.0
github.com/robfig/config v0.0.0-20141207224736-0f78529c8c7e
github.com/twinj/uuid v1.0.0 // indirect
github.com/xeonx/timeago v1.0.0-rc4 // indirect
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79
golang.org/x/net v0.0.0-20210324205630-d1beb07c2056 // indirect
golang.org/x/tools v0.0.0-20200219054238-753a1d49df85
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22
gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect
gopkg.in/stack.v0 v0.0.0-20141108040640-9b43fcefddd0 // indirect
)

150
go.sum Normal file
View File

@@ -0,0 +1,150 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/squirrel v1.3.0/go.mod h1:yaPeOnPG5ZRwL9oKdTsO/prlkPbXWZlRVMQ/gGlzIuA=
github.com/PuerkitoBio/goquery v1.6.1 h1:FgjbQZKl5HTmcn4sKBgvx8vv63nhyhIpv7lJpFGCWpk=
github.com/PuerkitoBio/goquery v1.6.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/agtorre/gocolorize v1.0.0 h1:TvGQd+fAqWQlDjQxSKe//Y6RaxK+RHpEU9X/zPmHW50=
github.com/agtorre/gocolorize v1.0.0/go.mod h1:cH6imfTkHVBRJhSOeSeEZhB4zqEYSq0sXuIyehgZMIY=
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b h1:L/QXpzIa3pOvUGt1D1lA5KjYhPBAN/3iWdP7xeFS9F0=
github.com/bradfitz/gomemcache v0.0.0-20190913173617-a41fca850d0b/go.mod h1:H0wQNHz2YrLsuXOZozoeDmnHXkNCRmMW0gwFWDfEZDA=
github.com/casbin/casbin v1.9.1/go.mod h1:z8uPsfBJGUsnkagrt3G8QvjgTKFMBJ32UP8HpZllfog=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/garyburd/redigo v1.6.2 h1:yE/pwKCrbLpLpQICzYTeZ7JsTA/C53wFTJHaEtRqniM=
github.com/garyburd/redigo v1.6.2/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-gorp/gorp v2.2.0+incompatible/go.mod h1:7IfkAQnO7jfT/9IQ3R9wL1dFhukN6aQxzKTHnkxzA/E=
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac h1:n1DqxAo4oWPMvH1+v+DLYlMCecgumhhgnxAPdqDIFHI=
github.com/inconshreveable/log15 v0.0.0-20201112154412-8562bdadbbac/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o=
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
github.com/klauspost/compress v1.10.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/myesui/uuid v1.0.0/go.mod h1:2CDfNgU0LR8mIdO8vdWd8i9gWWxLlcoIGGpSNgafq84=
github.com/newrelic/go-agent v3.4.0+incompatible/go.mod h1:a8Fv1b/fYhFSReoTU6HDkTYIMZeSVNffmoS726Y0LzQ=
github.com/patrickmn/go-cache v1.0.0 h1:3gD5McaYs9CxjyK5AXGcq8gdeCARtd/9gJDUvVeaZ0Y=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/poy/onpar v0.0.0-20200406201722-06f95a1c68e8/go.mod h1:nSbFQvMj97ZyhFRSJYtut+msi4sOY6zJDGCdSc+/rZU=
github.com/revel/cmd v1.0.3 h1:76YrotXjCcHI+oY9UtV7VP2SMsPfvDyet+AstcLYXoY=
github.com/revel/cmd v1.0.3/go.mod h1:44aFxD+KIr2w22yamS179ZrdyupHEXyko+2IsPK7+Wk=
github.com/revel/config v0.21.0/go.mod h1:GT4a9px5kDGRqLizcw/md0QFErrhen76toz4qS3oIoI=
github.com/revel/config v1.0.0 h1:UAzLPQ+x9nJeP6a+H93G+AKEosg3OO2oVLBXK9oSN2U=
github.com/revel/config v1.0.0/go.mod h1:GT4a9px5kDGRqLizcw/md0QFErrhen76toz4qS3oIoI=
github.com/revel/cron v0.21.0/go.mod h1:WrSp8p1H1IfOGumbbDGrGf8dZLjNSnGSnwxTj3nG80I=
github.com/revel/log15 v2.11.20+incompatible h1:JkA4tbwIo/UGEMumY50zndKq816RQW3LQ0wIpRc+32U=
github.com/revel/log15 v2.11.20+incompatible/go.mod h1:l0WmLRs+IM1hBl4noJiBc2tZQiOgZyXzS1mdmFt+5Gc=
github.com/revel/modules v0.21.0/go.mod h1:UBlNmO9VGZo4j6Ptn2uC/26Iclefuic+V40jYRPBxQE=
github.com/revel/modules v1.0.0 h1:JrqUDCU7y8Qfxrk+XcvhTUq8fv0hfzTCHNFSWYpsFBg=
github.com/revel/modules v1.0.0/go.mod h1:wJwbm8ccPzf+a5LBv49x/6a0TdIqh/XVad8u6w+RW8E=
github.com/revel/pathtree v0.0.0-20140121041023-41257a1839e9 h1:/d6kfjzjyx19ieWqMOXHSTLFuRxLOH15ZubtcAXExKw=
github.com/revel/pathtree v0.0.0-20140121041023-41257a1839e9/go.mod h1:TmlwoRLDvgRjoTe6rbsxIaka/CulzYrgfef7iNJcEWY=
github.com/revel/revel v0.21.0/go.mod h1:VZWJnHjpDEtuGUuZJ2NO42XryitrtwsdVaJxfDeo5yc=
github.com/revel/revel v1.0.0 h1:BsPFnKuuzXEkPtrjdjZHiDcvDmbBiBQvh7Z5c6kLb/Y=
github.com/revel/revel v1.0.0/go.mod h1:VZWJnHjpDEtuGUuZJ2NO42XryitrtwsdVaJxfDeo5yc=
github.com/robfig/config v0.0.0-20141207224736-0f78529c8c7e h1:3/9k/etUfgykjM3Rx8X0echJzo7gNNeND/ubPkqYw1k=
github.com/robfig/config v0.0.0-20141207224736-0f78529c8c7e/go.mod h1:Zerq1qYbCKtIIU9QgPydffGlpYfZ8KI/si49wuTLY/Q=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/twinj/uuid v1.0.0 h1:fzz7COZnDrXGTAOHGuUGYd6sG+JMq+AoE7+Jlu0przk=
github.com/twinj/uuid v1.0.0/go.mod h1:mMgcE1RHFUFqe5AfiwlINXisXfDGro23fWdPUfOMjRY=
github.com/tylerb/gls v0.0.0-20150407001822-e606233f194d/go.mod h1:0MwyId/pXK5wkYYEXe7NnVknX+aNBuF73fLV3U0reU8=
github.com/tylerb/is v2.1.4+incompatible/go.mod h1:3Bw2NWEEe8Kx7/etYqgm9ug53iNDgabnloch75jjOSc=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.12.0/go.mod h1:229t1eWu9UXTPmoUkbpN/fctKPBY4IJoFXQnxHGXy6E=
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
github.com/xeonx/timeago v1.0.0-rc4 h1:9rRzv48GlJC0vm+iBpLcWAr8YbETyN9Vij+7h2ammz4=
github.com/xeonx/timeago v1.0.0-rc4/go.mod h1:qDLrYEFynLO7y5Ho7w3GwgtYgpy5UfhcXIIQvMKVDkA=
github.com/yosssi/ace v0.0.5/go.mod h1:ALfIzm2vT7t5ZE7uoIZqF3TQ7SAOyupFZnkrF5id+K0=
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79 h1:IaQbIIB2X/Mp/DKctl6ROxz1KyMlKp4uyvL6+kQ7C88=
golang.org/x/crypto v0.0.0-20200429183012-4b2356b1ed79/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20210324205630-d1beb07c2056 h1:sANdAef76Ioam9aQUUdcAqricwY/WUaMc4+7LY4eGg8=
golang.org/x/net v0.0.0-20210324205630-d1beb07c2056/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501145240-bc7a7d42d5c3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492 h1:Paq34FxTluEPvVyayQqMPgHm+vTOrIifmcYxFBx9TLg=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20200219054238-753a1d49df85 h1:XNHaQ2CZDl/SjEZlUXGh7+OQvfLuFgmk3oNWkCFfERE=
golang.org/x/tools v0.0.0-20200219054238-753a1d49df85/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify/fsnotify.v1 v1.4.7/go.mod h1:Fyux9zXlo4rWoMSIzpn9fDAYjalPqJ/K1qJ27s+7ltE=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce h1:xcEWjVhvbDy+nHP67nPDDpbYrY+ILlfndk4bRioVHaU=
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw=
gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/stack.v0 v0.0.0-20141108040640-9b43fcefddd0 h1:lMH45EKqD8Nf6LwoF+43YOKjOAEEHQRVgDyG8RCV4MU=
gopkg.in/stack.v0 v0.0.0-20141108040640-9b43fcefddd0/go.mod h1:kl/bNzW/jgTgUOCGDj3XPn9/Hbfhw6pjfBRUnaTioFQ=
gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@@ -2,7 +2,7 @@
# album image # album image
Images=Imágenes Images=Imágenes
Upload=Subir Upload=Subir
Image URL=URL de imágen Image URL=URL de imagen
Albums=Álbumes Albums=Álbumes
Default=Predeterminado Default=Predeterminado
File title search=Búsqueda de título de archivo File title search=Búsqueda de título de archivo
@@ -24,8 +24,8 @@ Add Success!=Añadido correctamente!
Rename Success!=Renombrado correctamente! Rename Success!=Renombrado correctamente!
Delete Success!=Eliminado correctamente! Delete Success!=Eliminado correctamente!
Are you sure to delete this image ?=Está seguro que desea eliminar esta imagen? Are you sure to delete this image ?=Está seguro que desea eliminar esta imagen?
click to remove this image=clic para eliminar esta imagem click to remove this image=clic para eliminar esta imagen
error=error error=error
Error=Error Error=Error
Prev=Anterior Prev=Anterior
Next=Siguiente Next=Siguiente

View File

@@ -31,7 +31,7 @@ aboutMe=Sobre mí
#domain #domain
domainSet=Dominio domainSet=Dominio
subDomain=Subdominio subDomain=Subdominio
domain=Domain personalizado domain=Dominio personalizado
# theme # theme
elegant=Elegante elegant=Elegante
@@ -41,12 +41,12 @@ openComment=Comentarios abiertos?
chooseComment=Comentarios del sistema chooseComment=Comentarios del sistema
disqusHelp=Por favor ingresa el id de Disqus. disqusHelp=Por favor ingresa el id de Disqus.
needHelp=Necesita ayuda? needHelp=Necesita ayuda?
blogLogoTips=Subir una imagen para reemplzar el título del blog blogLogoTips=Subir una imagen para reemplazar el título del blog
saveSuccess=Guardado saveSuccess=Guardado
community=Comunidad community=Comunidad
home=Inicio home=Inicio
none=None none=Nada
moreShare=Más moreShare=Más
sinaWeibo=Weibo sinaWeibo=Weibo
weixin=Weichat weixin=Weichat
@@ -76,12 +76,12 @@ reportReason=Razón
chooseReason=Seleccione razón de reporte chooseReason=Seleccione razón de reporte
reportSuccess=Reportado correctamente, será notificado al autor, gracias por su aporte reportSuccess=Reportado correctamente, será notificado al autor, gracias por su aporte
error=Error error=Error
reportComment?=Reportar este comenatario? reportComment?=Reportar este comentario?
reportBlog?=Reportar este blog? reportBlog?=Reportar este blog?
confirmDeleteComment=Está seguro? confirmDeleteComment=Está seguro?
scanQRCode=Abrir wechat y escanear el código QR scanQRCode=Abrir wechat y escanear el código QR
justNow=Justo ahora justNow=Justo ahora
minutesAgo=minustos antes minutesAgo=minutos antes
hoursAgo=horas antes hoursAgo=horas antes
daysAgo=días antes daysAgo=días antes
weeksAgo=semanas antes weeksAgo=semanas antes

View File

@@ -16,13 +16,13 @@ enter code here=ingrese aquí el código
Image=Imagen Image=Imagen
Heading=Encabezado Heading=Encabezado
Numbered List=Lista numerada Numbered List=Lista numerada
Bulleted List=Lista de viñatas Bulleted List=Lista de viñetas
List item=Elemento de lista List item=Elemento de lista
Horizontal Rule=Regla horizontal Horizontal Rule=Regla horizontal
Markdown syntax=Sintaxis Markdown Markdown syntax=Sintaxis Markdown
Undo=Deshacer Undo=Deshacer
Redo=Rehacer Redo=Rehacer
enter image description here=ingrese aquí la descrición de imagen enter image description here=ingrese aquí la descripción de imagen
enter link description here=ingrese aquí descripción del enlace Web enter link description here=ingrese aquí descripción del enlace Web
Edit mode=Modo edición Edit mode=Modo edición
Vim mode=Modo Vim Vim mode=Modo Vim

View File

@@ -11,7 +11,7 @@ My Group=Mis grupos
# post abstract # post abstract
Abstract=Resumen Abstract=Resumen
Description=Desripción Description=Descripción
Once the abstract has been updated, it will not set the abstract automatically other than you cancel it.=Una vez actualizado el resumen, no se establecerá el resumen automáticamente salvo que lo cancele. Once the abstract has been updated, it will not set the abstract automatically other than you cancel it.=Una vez actualizado el resumen, no se establecerá el resumen automáticamente salvo que lo cancele.
Main Image=Imagen principal Main Image=Imagen principal
Get next image as main image from content=Obtener la siguiente imagen como imagen principal del contenido Get next image as main image from content=Obtener la siguiente imagen como imagen principal del contenido
@@ -26,13 +26,13 @@ normalAccountType=Normal
imageSize=Capacidad de imágenes imageSize=Capacidad de imágenes
attachSize=Capacidad de adjuntos attachSize=Capacidad de adjuntos
totalTraffic=Tráfico Web totalTraffic=Tráfico Web
upgrade=Upgrade Mis cuentas upgrade=Actualizar Mis cuentas
leanoteEvents=Noticias Leanote leanoteEvents=Noticias Leanote
addLeanoteAccount=New Leanote Account addLeanoteAccount=Nueva cuenta Leanote
defaultComment=Comentarios del sistema leanote por defecto defaultComment=Comentarios del sistema leanote por defecto
upgradeAccountTips=Quiero utilizar un dominio personalizado para mi blog, <a class="btn btn-default" href="/service">Actualizar mi cuenta</a> upgradeAccountTips=Quiero utilizar un dominio personalizado para mi blog, <a class="btn btn-default" href="/service">Actualizar mi cuenta</a>
cateIsPublicNotebook=Categoría son los cuadernos publicados cateIsPublicNotebook=Categoría son los cuadernos publicados
dragAndSort=Arrástre para ordenar dragAndSort=Arrastre para ordenar
permanentLink=Enlace permanente permanentLink=Enlace permanente
cate=Categoría cate=Categoría
noCates=Sin categorías noCates=Sin categorías
@@ -41,7 +41,7 @@ singleTips=Puede añadir muchas páginas individuales
addSingle=Nueva página simple addSingle=Nueva página simple
updateSingle=Actualizar página simple updateSingle=Actualizar página simple
inputSingleTitle=Es requerido un título de página simple inputSingleTitle=Es requerido un título de página simple
saveSort=Guardar secuencua saveSort=Guardar secuencia
pagingAndSort=Configuración de paginación pagingAndSort=Configuración de paginación
perPageSize=Por tamaño de página perPageSize=Por tamaño de página
sortField=Campo ordenado sortField=Campo ordenado
@@ -71,14 +71,14 @@ myOtherThemes=Mis otros temas
leanoteThemeMarket=Tienda de temas Leanote leanoteThemeMarket=Tienda de temas Leanote
updateTheme=Actualizar tema updateTheme=Actualizar tema
tplStyleScript=plantilla, estilo, script tplStyleScript=plantilla, estilo, script
newFile=New file newFile=Nuevo archivo
image=Image image=Imagen
currentFile=Current file currentFile=Archivo actual
tpl=Template tpl=Plantilla
style=Style style=Estilo
script=Script script=Script
header=Encabezado header=Encabezado
footer=Píe de página footer=Pie de página
index=Inicio index=Inicio
cate=Categoría cate=Categoría
search=Buscar search=Buscar
@@ -104,7 +104,7 @@ Blog Base Info=Base Info Blog
Comment=Comentario Comment=Comentario
Posts=Posts Posts=Posts
Domain=Dominio Domain=Dominio
Paging=Paging Paging=Paginado
Category=Categoría Category=Categoría
Add Single=Añadir simple Add Single=Añadir simple
Update Single=Actualizar simple Update Single=Actualizar simple
@@ -115,7 +115,7 @@ Upate Theme=Actualizar tema
group=Grupo group=Grupo
newGroup=Nuevo grupo newGroup=Nuevo grupo
deleteGroup=Eliminar grupo deleteGroup=Eliminar grupo
addMemberTips=Input username or email to add member addMemberTips=Ingresar nombre de usuario o correo electrónico para agregar un miembro
deleteMember=Eliminar miembro deleteMember=Eliminar miembro
forbiddenNotMyGroup=No tienes autorización para acceder, porque no eres el propietario del grupo forbiddenNotMyGroup=No tienes autorización para acceder, porque no eres el propietario del grupo
userExistsInGroup=El miembro ya existe en este grupo userExistsInGroup=El miembro ya existe en este grupo
@@ -124,7 +124,7 @@ notMyGroup=El miembro ya existe en este grupo
# view js # view js
Are you sure ?=Está seguro? Are you sure ?=Está seguro?
Are you sure to install it ?=Esta seguro de instalar? Are you sure to install it ?=Está seguro de instalar?
Are you sure to delete=está seguro para eliminar? Are you sure to delete=está seguro para eliminar?
Success=Exítoso Success=Exítoso
Error=Error Error=Error

View File

@@ -17,20 +17,20 @@ home=Inicio
Leanote Explore=Explorar Leanote Leanote Explore=Explorar Leanote
desktopApp=App desktopApp=App
aboutLeanote=Sobre Leanote aboutLeanote=Sobre Leanote
suggestions=Sugerencas suggestions=Sugerencias
yourSuggestions=Sugerencias yourSuggestions=Sugerencias
leanoteBlog=Blog leanoteBlog=Blog
knowledge=Conocimiento knowledge=Conocimiento
knowledgeInfo=Use Leanote como una nota, maneje su conocimiento en Leanote. knowledgeInfo=Use Leanote como una nota, maneje su conocimiento en Leanote.
share=Compartir share=Compartir
shareInfo=Comparte tus conocimientos a tus amigos en Leanote. shareInfo=Comparte sus conocimientos con sus amigos en Leanote.
cooperation=Cooperación cooperation=Cooperación
cooperationInfo=Colabore con sus amigos para mejorar su conocimiento. cooperationInfo=Colabore con sus amigos para mejorar su conocimiento.
blog=Blog blog=Blog
blogInfo=Puede publicar su conocimiento y leanote es su blog! blogInfo=Puede publicar su conocimiento y leanote es su blog!
suggestionsInfo=ayude a mejorar nuestro servicio. suggestionsInfo=ayude a mejorar nuestro servicio.
yourContact=Su contacto yourContact=Su contacto
emailOrOthers=Email or other contact way emailOrOthers=Correo electrónico u otra forma de contacto
captcha=Captcha captcha=Captcha
reloadCaptcha=Recargar Captcha reloadCaptcha=Recargar Captcha
captchaError=No coincide Captcha captchaError=No coincide Captcha
@@ -70,17 +70,17 @@ registerSuccessAndRdirectToNote=Reistro exítoso, redireccionando...
userHasBeenRegistered=%s ya está registrado userHasBeenRegistered=%s ya está registrado
# find password # find password
passwordTips=La longitud es al menos de 6 carácteres passwordTips=La longitud es al menos de 6 caracteres
findPassword=Recuperar contraseña findPassword=Recuperar contraseña
findPasswordSendEmailOver=Hemos enviado el enlace de recuperación de contraseña a tu correo electrónico, consulta tu correo electrónico findPasswordSendEmailOver=Hemos enviado el enlace de recuperación de contraseña a tu correo electrónico, consulta tu correo electrónico
checkEmai=Verificar email checkEmai=Verificar correo electrónico
findPasswordTimeout=Sesión terminada findPasswordTimeout=Sesión terminada
reFindPassword=recuperar contraseña de nuevo reFindPassword=recuperar contraseña de nuevo
updatePassword=Actualizar contraseña updatePassword=Actualizar contraseña
updatePasswordSuccessRedirectToLogin=actualización de contraseña exítoso y redireccionando a la página de inicio... updatePasswordSuccessRedirectToLogin=actualización de contraseña exítoso y redireccionando a la página de inicio...
inputPassword2=Por favor ingrese su contraseña de nuevo inputPassword2=Por favor ingrese su contraseña de nuevo
confirmPassword=Por favor confirme su contraseña confirmPassword=Por favor confirme su contraseña
notGoodPassword=No es una contraseña válidad, debe ser al menos de 6 carácteres notGoodPassword=No es una contraseña válida, debe ser al menos de 6 caracteres
# note # note
myBlog=Blog myBlog=Blog
@@ -91,7 +91,7 @@ editorTips=Consejos
editorTipsInfo=<h4>1. Atajos</h4>ctrl+shift+c Cambiar a código<h4>2. shift+enter Salir del bloque actual</h4> eg. <img src="/images/outofcode.png" style="width: 90px"/> en esta situación puede usar shift + enter para salir del bloque de código actual. editorTipsInfo=<h4>1. Atajos</h4>ctrl+shift+c Cambiar a código<h4>2. shift+enter Salir del bloque actual</h4> eg. <img src="/images/outofcode.png" style="width: 90px"/> en esta situación puede usar shift + enter para salir del bloque de código actual.
newNote=Nueva nota newNote=Nueva nota
newMarkdownNote=Nueva nota Markdown newMarkdownNote=Nueva nota Markdown
noNoteNewNoteTips=El cuaderno está vac, por qué no ... noNoteNewNoteTips=El cuaderno está vacío, por qué no ...
canntNewNoteTips=Lo sentimos, no puede agregar notas aquí, por favor elija primero un cuaderno. canntNewNoteTips=Lo sentimos, no puede agregar notas aquí, por favor elija primero un cuaderno.
new=Nuevo new=Nuevo
newMarkdown=Nueva nota markdown newMarkdown=Nueva nota markdown
@@ -101,7 +101,7 @@ note=Nota
myNotebook=Mis cuadernos myNotebook=Mis cuadernos
addNotebook=Añadir cuaderno addNotebook=Añadir cuaderno
search=Buscar search=Buscar
clearSearch=Limpiar busqueda clearSearch=Limpiar búsqueda
all=Reciente all=Reciente
trash=Papelera trash=Papelera
delete=Eliminar delete=Eliminar
@@ -143,8 +143,8 @@ green=verde
accountSetting=Cuenta accountSetting=Cuenta
logout=Salir logout=Salir
basicInfo=Básico basicInfo=Básico
basicInfoSet=Configuracion básica basicInfoSet=Configuración básica
updateEmail=Actualizar email updateEmail=Actualizar correo electrónico
usernameSetting=Actualizar nombre de usuario usernameSetting=Actualizar nombre de usuario
chooseImage=Seleccionar imagen chooseImage=Seleccionar imagen
oldPassword=Contraseña antigua oldPassword=Contraseña antigua
@@ -186,17 +186,17 @@ copyToMyNotebook=Copiar a mi cuaderno
# note-dev # note-dev
emailInSending=Enviando a emailInSending=Enviando a
checkEmail=Verifique el email checkEmail=Verifique el correo electrónico
setUsername=Enviar usuario setUsername=Enviar usuario
setUsernameTips=Su email actual es: <code>%s</code>. Puede establecer su nombre de usuario. <br />Nombre de usuarios debe tener por lo mínimo cuatro carácteres y no contener carácteres especiales. setUsernameTips=Su correo electrónico actual es: <code>%s</code>. Puede establecer su nombre de usuario. <br />Nombre de usuarios debe tener por lo mínimo cuatro carácteres y no contener carácteres especiales.
currentEmail=Su correo electrónico es: <code>%s</code> currentEmail=Su correo electrónico es: <code>%s</code>
updateEmail=Actualizar email updateEmail=Actualizar correo electrónico
updateEmailTips=Debe verificar el correo electrónico después de actualizar el correo electrónico. El correo verificado será su nueva cuenta. updateEmailTips=Debe verificar el correo electrónico después de actualizar el correo electrónico. El correo verificado será su nueva cuenta.
sendVerifiedEmail=Envia un correo electronico de verificación sendVerifiedEmail=Envia un correo electrónico de verificación
verified=Verificado verified=Verificado
unVerified=No verificado unVerified=No verificado
verifiedNow=Verificar ahora verifiedNow=Verificar ahora
resendVerifiedEmail=Reeneviar email de verificación resendVerifiedEmail=Reenviar email de verificación
# share # share
defaulthhare=Predeterminado defaulthhare=Predeterminado
@@ -211,7 +211,7 @@ sendInviteEmailToYourFriend=Enviar invitación al email de su amigo
copySuccess=Copiado copySuccess=Copiado
copyFailed=Copiado fallido copyFailed=Copiado fallido
friendNotExits=Tu amigo no tiene la cuenta de %s's, invita a registrarse en el enlace: %s friendNotExits=Tu amigo no tiene la cuenta de %s's, invita a registrarse en el enlace: %s
emailBodyRequired=Requerdio el mensaje del Email emailBodyRequired=Requerido el mensaje del Email
clickToCopy=Clic para copiar clickToCopy=Clic para copiar
sendSuccess=exítoso sendSuccess=exítoso
inviteEmailBody=Hola,Yo soy %s, %s es impresionante, venga! inviteEmailBody=Hola,Yo soy %s, %s es impresionante, venga!
@@ -239,12 +239,12 @@ emailSendFailed=Envió de Email fallido
inputPassword=Requerida la contraseña inputPassword=Requerida la contraseña
inputNewPassword=Requerida la nueva contraseña inputNewPassword=Requerida la nueva contraseña
inputPassword2=Introduzca de nuevo la nueva contraseña inputPassword2=Introduzca de nuevo la nueva contraseña
errorPassword=La longitud de la contraseña es al menos 6 caráteres y asegúrese de ser lo más compleja posible errorPassword=La longitud de la contraseña es al menos 6 caracteres y asegúrese de ser lo más compleja posible
oldPasswordError=La contraseña antigua no coincide oldPasswordError=La contraseña antigua no coincide
confirmPassword=Las contraseñas no coinciden confirmPassword=Las contraseñas no coinciden
updatePasswordSuccess=Actualziación de contraseña correcta updatePasswordSuccess=Actualización de contraseña correcta
errorDomain=El dominio personalizado erróneo, eg. www.myblog.com errorDomain=El dominio personalizado erróneo, eg. www.myblog.com
domainExisted=El dominigio personalziado ya existe domainExisted=El dominio personalizado ya existe
errorSubDomain=Introduzca el subdominio válido, la longitud es de al menos 4 y no hay caracteres especiales errorSubDomain=Introduzca el subdominio válido, la longitud es de al menos 4 y no hay caracteres especiales
subDomainExisted=Subdominio ya existe subDomainExisted=Subdominio ya existe
domainNotPointToLeanote=El dominio personalizado no ha señalado a d.leanote.com domainNotPointToLeanote=El dominio personalizado no ha señalado a d.leanote.com

View File

@@ -6,7 +6,7 @@ Paste=Pegar
Close=Cerrar Close=Cerrar
Font Family=Familia de fuente Font Family=Familia de fuente
Pre=Pre Pre=Pre
Align right=Aliear a la derecha Align right=Alinear a la derecha
New document=Nuevo documento New document=Nuevo documento
Blockquote=Cita Blockquote=Cita
Numbered list=Lista numerada Numbered list=Lista numerada
@@ -23,7 +23,7 @@ Header 1=Encabezado 1
Superscript=Superíndice Superscript=Superíndice
Clear formatting=Limpiar formato Clear formatting=Limpiar formato
Font Sizes=Tamaños de fuentes Font Sizes=Tamaños de fuentes
Subscript=Subindice Subscript=Subíndice
Header 6=Encabezado 6 Header 6=Encabezado 6
Redo=Rehacer Redo=Rehacer
Paragraph=Párrafo Paragraph=Párrafo
@@ -46,7 +46,7 @@ Visual aids=Ayuda visuales
Lower Greek=Letra griega Lower Greek=Letra griega
Square=Cuadrado Square=Cuadrado
Default=Predeterminado Default=Predeterminado
Lower Alpha=Alfa miníscula Lower Alpha=Alfa minúscula
Circle=Círculo Circle=Círculo
Disc=Disc Disc=Disc
Upper Alpha=Alfa mayúscula Upper Alpha=Alfa mayúscula
@@ -54,7 +54,7 @@ Upper Roman=Romana mayúscula
Lower Roman=Romanana minúscula Lower Roman=Romanana minúscula
Name=Nombre Name=Nombre
Anchor=Marcador Anchor=Marcador
You have unsaved changes are you sure you want to navigate away?=¿Tiene cambios sin guardar, stá seguro que desea salir? You have unsaved changes are you sure you want to navigate away?=¿Tiene cambios sin guardar, está seguro que desea salir?
Restaurar último borrador = Restaurar último borrador Restaurar último borrador = Restaurar último borrador
Special character=Carácter especial Special character=Carácter especial
Source code=Código fuente Source code=Código fuente
@@ -64,7 +64,7 @@ Emoticons=Emoticones
Robots=Robots Robots=Robots
Document properties=Propiedades del documento Document properties=Propiedades del documento
Title=Título Title=Título
Keywords=Palabras clase Keywords=Palabras clave
Encoding=Codificación Encoding=Codificación
Description=Descripción Description=Descripción
Author=Autor Author=Autor
@@ -98,7 +98,7 @@ The URL you entered seems to be an email address. Do you want to add the require
Insert/edit link=Insertar/editar enlace Web Insert/edit link=Insertar/editar enlace Web
Insert/edit video=Insertar/editar video Insert/edit video=Insertar/editar video
Poster=Poster Poster=Poster
Alternative source=Codigo alternativo Alternative source=Código alternativo
Paste your embed code below:=Pegue su código embebido aquí: Paste your embed code below:=Pegue su código embebido aquí:
Insert video=Insertar video Insert video=Insertar video
Embed=Embeber Embed=Embeber
@@ -111,7 +111,7 @@ Save=Guardar
Could not find the specified string.=No puede encontrar la cadena especificada. Could not find the specified string.=No puede encontrar la cadena especificada.
Replace=Reemplace Replace=Reemplace
Next=Siguiente Next=Siguiente
Whole words=Palbras completas Whole words=Palabras completas
Find and replace=Encontrar y reemplazar Find and replace=Encontrar y reemplazar
Replace with=Reemplazar con Replace with=Reemplazar con
Find=Encontrar Find=Encontrar
@@ -157,9 +157,9 @@ Cols=Columnas
Insert row after=Insertar filas después Insert row after=Insertar filas después
Width=Ancho Width=Ancho
Cell properties=Propiedades de celda Cell properties=Propiedades de celda
Left=Inzquierda Left=Izquierda
Cut row=Cortar fila Cut row=Cortar fila
Delete column=Elimar columna Delete column=Eliminar columna
Center=Centrar Center=Centrar
Merge cells=Unir celdas Merge cells=Unir celdas
Insert template=Insertar plantilla Insert template=Insertar plantilla
@@ -167,7 +167,7 @@ Templates=Plantillas
Background color=Color de fondo Background color=Color de fondo
Text color=Color de texto Text color=Color de texto
Show blocks=Mostrar cita Show blocks=Mostrar cita
Show invisible characters=Mostrar carécteres ocultos Show invisible characters=Mostrar caracteres ocultos
Words: {0}=Palabras: {0} Words: {0}=Palabras: {0}
Insert=Insertar Insert=Insertar
File=Archivo File=Archivo

View File

@@ -248,7 +248,7 @@ function genNav() {
var text = $(hs[i]).text(); var text = $(hs[i]).text();
var tagName = hs[i].tagName.toLowerCase(); var tagName = hs[i].tagName.toLowerCase();
// scrollTo在page.js中定义 // scrollTo在page.js中定义
titles += '<li class="nav-' + tagName + '"><a data-a="' + tagName + '-' + encodeURI(text)+'" onclick="scrollTo(this, \'' + tagName + '\', \'' + text + '\')">' + text + '</a></li>'; titles += '<li class="nav-' + tagName + '"><a data-a="' + tagName + '-' + encodeURI(text)+'" onclick="window.scrollTo(this, \'' + tagName + '\', \'' + text + '\')">' + text + '</a></li>';
} }
titles += "</ul>"; titles += "</ul>";
$("#blogNavContent").html(titles); $("#blogNavContent").html(titles);

View File

@@ -890,6 +890,12 @@ a:hover {
.ace_invisible_space { .ace_invisible_space {
visibility: hidden !important; visibility: hidden !important;
} }
.mce-match-marker.mce-match-marker-selected {
background: yellow;
}
#mce-modal-block {
display: none;
}
#editor, #editor,
#mdEditor { #mdEditor {
position: absolute; position: absolute;

View File

@@ -7,6 +7,13 @@
visibility: hidden !important; visibility: hidden !important;
} }
.mce-match-marker.mce-match-marker-selected {
background: yellow;
}
#mce-modal-block {
display: none;
}
#editor, #mdEditor{ #editor, #mdEditor{
position: absolute; position: absolute;
z-index: 2; z-index: 2;

View File

@@ -890,6 +890,12 @@ a:hover {
.ace_invisible_space { .ace_invisible_space {
visibility: hidden !important; visibility: hidden !important;
} }
.mce-match-marker.mce-match-marker-selected {
background: yellow;
}
#mce-modal-block {
display: none;
}
#editor, #editor,
#mdEditor { #mdEditor {
position: absolute; position: absolute;

View File

@@ -890,6 +890,12 @@ a:hover {
.ace_invisible_space { .ace_invisible_space {
visibility: hidden !important; visibility: hidden !important;
} }
.mce-match-marker.mce-match-marker-selected {
background: yellow;
}
#mce-modal-block {
display: none;
}
#editor, #editor,
#mdEditor { #mdEditor {
position: absolute; position: absolute;

View File

@@ -890,6 +890,12 @@ a:hover {
.ace_invisible_space { .ace_invisible_space {
visibility: hidden !important; visibility: hidden !important;
} }
.mce-match-marker.mce-match-marker-selected {
background: yellow;
}
#mce-modal-block {
display: none;
}
#editor, #editor,
#mdEditor { #mdEditor {
position: absolute; position: absolute;

File diff suppressed because one or more lines are too long

View File

@@ -548,7 +548,7 @@ function initEditor() {
"searchreplace leanote_nav leanote_code tabfocus", "searchreplace leanote_nav leanote_code tabfocus",
"table textcolor" ], // nonbreaking directionality charmap "table textcolor" ], // nonbreaking directionality charmap
toolbar1 : "formatselect | forecolor backcolor | bold italic underline strikethrough | leaui_image leaui_mindmap | leanote_code leanote_inline_code | bullist numlist | alignleft aligncenter alignright alignjustify", toolbar1 : "formatselect | forecolor backcolor | bold italic underline strikethrough | leaui_image leaui_mindmap | leanote_code leanote_inline_code | bullist numlist | alignleft aligncenter alignright alignjustify",
toolbar2 : "outdent indent blockquote | link unlink | table | hr removeformat | subscript superscript |searchreplace | pastetext | leanote_ace_pre | fontselect fontsizeselect", toolbar2 : "outdent indent blockquote | link unlink | table | hr removeformat | subscript superscript | searchreplace | pastetext | leanote_ace_pre | fontselect fontsizeselect",
// 使用tab键: http://www.tinymce.com/wiki.php/Plugin3x:nonbreaking // 使用tab键: http://www.tinymce.com/wiki.php/Plugin3x:nonbreaking
// http://stackoverflow.com/questions/13543220/tiny-mce-how-to-allow-people-to-indent // http://stackoverflow.com/questions/13543220/tiny-mce-how-to-allow-people-to-indent
@@ -1030,7 +1030,7 @@ LeaAce = {
var classes = $pre.attr('class') || ''; var classes = $pre.attr('class') || '';
var isHtml = classes.indexOf('brush:html') != -1; var isHtml = classes.indexOf('brush:html') != -1;
if($pre.attr('style') || if($pre.attr('style') ||
(!isHtml && $pre.html().indexOf('style') != -1)) { // 如果是html就不用考虑了, 因为html格式的支持有style (!isHtml && $pre.html().indexOf('<style>') != -1)) { // 如果是html就不用考虑了, 因为html格式的支持有style
$pre.html($pre.text()); $pre.html($pre.text());
} }
$pre.find('.toggle-raw').remove(); $pre.find('.toggle-raw').remove();

View File

@@ -0,0 +1 @@
var MSG={"noTag":"Sin etiquetas","saveSuccess":"Guardado","none":"None","like":"Me gusta","unlike":"No me gusta","delete":"Eliminar","cancel":"Cancelar","confirm":"Confirmar","chooseReason":"Seleccione razón de reporte","reportSuccess":"Reportado correctamente, será notificado al autor, gracias por su aporte","error":"Error","reportComment?":"Reportar este comenatario?","reportBlog?":"Reportar este blog?","confirmDeleteComment":"Está seguro?","scanQRCode":"Abrir wechat y escanear el código QR","justNow":"Justo ahora","minutesAgo":"minustos antes","hoursAgo":"horas antes","daysAgo":"días antes","weeksAgo":"semanas antes","monthsAgo":"meses antes"};function getMsg(key, data) {var msg = MSG[key];if(msg) {if(data) {if(!isArray(data)) {data = [data];}for(var i = 0; i < data.length; ++i) {msg = msg.replace("%s", data[i]);}}return msg;}return key;}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -49,6 +49,7 @@ define('history', [], function() {
if (this.note.IsMarkdown) { if (this.note.IsMarkdown) {
wrap = '<pre>'; wrap = '<pre>';
wrapEnd = '</pre>'; wrapEnd = '</pre>';
content = trimTitle(content) // for xss
} }
$historyContent.html(wrap + content + wrapEnd); $historyContent.html(wrap + content + wrapEnd);

View File

@@ -13228,7 +13228,7 @@ define('extensions/scrollLink',[
return; return;
} }
evt.preventDefault(); evt.preventDefault();
var anchorElt = $('#preview-contents ' + id); var anchorElt = $('#preview-contents ' + decodeURIComponent(id));
if(!anchorElt.length) { if(!anchorElt.length) {
return; return;
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -22,7 +22,7 @@ tinymce.PluginManager.add('leanote_nav', function(editor) {
var text = $(hs[i]).text(); var text = $(hs[i]).text();
var tagName = hs[i].tagName.toLowerCase(); var tagName = hs[i].tagName.toLowerCase();
// scrollTo在page.js中定义 // scrollTo在page.js中定义
titles += '<li class="nav-' + tagName + '"><a data-a="' + tagName + '-' + encodeURI(text)+'" onclick="scrollTo(this, \'' + tagName + '\', \'' + text + '\')">' + text + '</a></li>'; titles += '<li class="nav-' + tagName + '"><a data-a="' + tagName + '-' + encodeURI(text)+'" onclick="window.scrollTo(this, \'' + tagName + '\', \'' + text + '\')">' + text + '</a></li>';
} }
titles += "</ul>"; titles += "</ul>";
$("#leanoteNavContent").html(titles).height("auto"); // auto $("#leanoteNavContent").html(titles).height("auto"); // auto

View File

@@ -1 +1 @@
tinymce.PluginManager.add("leanote_nav",function(a){function b(){var b=a.getBody(),d=$(b),e=d.html();if(c!=e){c=e;for(var f=d.find("h1,h2,h3,h4,h5,h6").toArray(),g="<ul>",h=0;h<f.length;++h){var i=$(f[h]).text(),j=f[h].tagName.toLowerCase();g+='<li class="nav-'+j+'"><a data-a="'+j+"-"+encodeURI(i)+'" onclick="scrollTo(this, \''+j+"', '"+i+"')\">"+i+"</a></li>"}g+="</ul>",$("#leanoteNavContent").html(g).height("auto"),f.length||$("#leanoteNavContent").html("&nbsp; Nothing...");var k=$("#leanoteNavContent").height(),l=$("#editorContent").height()-29;k>l&&$("#leanoteNavContent").height(l)}}var c="";a.on("init",function(){window.setTimeout(function(){b(),a.on("setcontent beforeadd undo paste",b),a.on("ExecCommand",function(a){b()}),a.on("click",function(a){b(),$("body").trigger("click")})},0)})}); tinymce.PluginManager.add("leanote_nav",function(a){function b(){var b=a.getBody(),d=$(b),e=d.html();if(c!=e){c=e;for(var f=d.find("h1,h2,h3,h4,h5,h6").toArray(),g="<ul>",h=0;h<f.length;++h){var i=$(f[h]).text(),j=f[h].tagName.toLowerCase();g+='<li class="nav-'+j+'"><a data-a="'+j+"-"+encodeURI(i)+'" onclick="window.scrollTo(this, \''+j+"', '"+i+"')\">"+i+"</a></li>"}g+="</ul>",$("#leanoteNavContent").html(g).height("auto"),f.length||$("#leanoteNavContent").html("&nbsp; Nothing...");var k=$("#leanoteNavContent").height(),l=$("#editorContent").height()-29;k>l&&$("#leanoteNavContent").height(l)}}var c="";a.on("init",function(){window.setTimeout(function(){b(),a.on("setcontent beforeadd undo paste",b),a.on("ExecCommand",function(a){b()}),a.on("click",function(a){b(),$("body").trigger("click")})},0)})});

View File

@@ -117,4 +117,4 @@
writeScripts(); writeScripts();
})(this); })(this);
// $hash: 75083770f38396c70840f96857825cd8 // $hash: ffd77a8423e40c34b0b8ff089b254d8f

View File

@@ -233,7 +233,7 @@ define("tinymce/pasteplugin/Clipboard", [
if(reIsOk(ret)) { if(reIsOk(ret)) {
// 将图片替换之 // 将图片替换之
// var src = urlPrefix + "/" + ret.Item; // var src = urlPrefix + "/" + ret.Item;
var src = urlPrefix + "/file/outputImage?fileId=" + ret.Id; var src = urlPrefix + "/api/file/getImage?fileId=" + ret.Id;
var dom = editor.dom; var dom = editor.dom;
for(var i in ids) { for(var i in ids) {
var id = ids[i]; var id = ids[i];
@@ -621,6 +621,8 @@ define("tinymce/pasteplugin/Clipboard", [
if (hasImage(e)) { if (hasImage(e)) {
removePasteBin(); removePasteBin();
// 不然会在内容中插入一个图片,
e.preventDefault();
return; return;
} }
@@ -1189,4 +1191,4 @@ define("tinymce/pasteplugin/Plugin", [
}); });
expose(["tinymce/pasteplugin/Utils","tinymce/pasteplugin/WordFilter"]); expose(["tinymce/pasteplugin/Utils","tinymce/pasteplugin/WordFilter"]);
})(this); })(this);

File diff suppressed because one or more lines are too long

View File

@@ -277,7 +277,9 @@
type: 'window', type: 'window',
layout: "flex", layout: "flex",
pack: "center", pack: "center",
align: "center", align: "right",
classes: "find-replace-ctn",
// minWidth: 300,
onClose: function() { onClose: function() {
editor.focus(); editor.focus();
self.done(); self.done();
@@ -362,6 +364,9 @@
] ]
} }
}).renderTo().reflow(); }).renderTo().reflow();
// 立即到最右
win.moveTo($('body').width()-$('.mce-find-replace-ctn').width(), 60)
} }
self.init = function(ed) { self.init = function(ed) {

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More