Show More
@@ -1,340 +1,340 b'' | |||||
1 | GNU GENERAL PUBLIC LICENSE |
|
1 | GNU GENERAL PUBLIC LICENSE | |
2 | Version 2, June 1991 |
|
2 | Version 2, June 1991 | |
3 |
|
3 | |||
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. |
|
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. | |
5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
6 | Everyone is permitted to copy and distribute verbatim copies |
|
6 | Everyone is permitted to copy and distribute verbatim copies | |
7 | of this license document, but changing it is not allowed. |
|
7 | of this license document, but changing it is not allowed. | |
8 |
|
8 | |||
9 | Preamble |
|
9 | Preamble | |
10 |
|
10 | |||
11 | The licenses for most software are designed to take away your |
|
11 | The licenses for most software are designed to take away your | |
12 | freedom to share and change it. By contrast, the GNU General Public |
|
12 | freedom to share and change it. By contrast, the GNU General Public | |
13 | License is intended to guarantee your freedom to share and change free |
|
13 | License is intended to guarantee your freedom to share and change free | |
14 | software--to make sure the software is free for all its users. This |
|
14 | software--to make sure the software is free for all its users. This | |
15 | General Public License applies to most of the Free Software |
|
15 | General Public License applies to most of the Free Software | |
16 | Foundation's software and to any other program whose authors commit to |
|
16 | Foundation's software and to any other program whose authors commit to | |
17 | using it. (Some other Free Software Foundation software is covered by |
|
17 | using it. (Some other Free Software Foundation software is covered by | |
18 | the GNU Library General Public License instead.) You can apply it to |
|
18 | the GNU Library General Public License instead.) You can apply it to | |
19 | your programs, too. |
|
19 | your programs, too. | |
20 |
|
20 | |||
21 | When we speak of free software, we are referring to freedom, not |
|
21 | When we speak of free software, we are referring to freedom, not | |
22 | price. Our General Public Licenses are designed to make sure that you |
|
22 | price. Our General Public Licenses are designed to make sure that you | |
23 | have the freedom to distribute copies of free software (and charge for |
|
23 | have the freedom to distribute copies of free software (and charge for | |
24 | this service if you wish), that you receive source code or can get it |
|
24 | this service if you wish), that you receive source code or can get it | |
25 | if you want it, that you can change the software or use pieces of it |
|
25 | if you want it, that you can change the software or use pieces of it | |
26 | in new free programs; and that you know you can do these things. |
|
26 | in new free programs; and that you know you can do these things. | |
27 |
|
27 | |||
28 | To protect your rights, we need to make restrictions that forbid |
|
28 | To protect your rights, we need to make restrictions that forbid | |
29 | anyone to deny you these rights or to ask you to surrender the rights. |
|
29 | anyone to deny you these rights or to ask you to surrender the rights. | |
30 | These restrictions translate to certain responsibilities for you if you |
|
30 | These restrictions translate to certain responsibilities for you if you | |
31 | distribute copies of the software, or if you modify it. |
|
31 | distribute copies of the software, or if you modify it. | |
32 |
|
32 | |||
33 | For example, if you distribute copies of such a program, whether |
|
33 | For example, if you distribute copies of such a program, whether | |
34 | gratis or for a fee, you must give the recipients all the rights that |
|
34 | gratis or for a fee, you must give the recipients all the rights that | |
35 | you have. You must make sure that they, too, receive or can get the |
|
35 | you have. You must make sure that they, too, receive or can get the | |
36 | source code. And you must show them these terms so they know their |
|
36 | source code. And you must show them these terms so they know their | |
37 | rights. |
|
37 | rights. | |
38 |
|
38 | |||
39 | We protect your rights with two steps: (1) copyright the software, and |
|
39 | We protect your rights with two steps: (1) copyright the software, and | |
40 | (2) offer you this license which gives you legal permission to copy, |
|
40 | (2) offer you this license which gives you legal permission to copy, | |
41 | distribute and/or modify the software. |
|
41 | distribute and/or modify the software. | |
42 |
|
42 | |||
43 | Also, for each author's protection and ours, we want to make certain |
|
43 | Also, for each author's protection and ours, we want to make certain | |
44 | that everyone understands that there is no warranty for this free |
|
44 | that everyone understands that there is no warranty for this free | |
45 | software. If the software is modified by someone else and passed on, we |
|
45 | software. If the software is modified by someone else and passed on, we | |
46 | want its recipients to know that what they have is not the original, so |
|
46 | want its recipients to know that what they have is not the original, so | |
47 | that any problems introduced by others will not reflect on the original |
|
47 | that any problems introduced by others will not reflect on the original | |
48 | authors' reputations. |
|
48 | authors' reputations. | |
49 |
|
49 | |||
50 | Finally, any free program is threatened constantly by software |
|
50 | Finally, any free program is threatened constantly by software | |
51 | patents. We wish to avoid the danger that redistributors of a free |
|
51 | patents. We wish to avoid the danger that redistributors of a free | |
52 | program will individually obtain patent licenses, in effect making the |
|
52 | program will individually obtain patent licenses, in effect making the | |
53 | program proprietary. To prevent this, we have made it clear that any |
|
53 | program proprietary. To prevent this, we have made it clear that any | |
54 | patent must be licensed for everyone's free use or not licensed at all. |
|
54 | patent must be licensed for everyone's free use or not licensed at all. | |
55 |
|
55 | |||
56 | The precise terms and conditions for copying, distribution and |
|
56 | The precise terms and conditions for copying, distribution and | |
57 | modification follow. |
|
57 | modification follow. | |
58 |
|
58 | |||
59 | GNU GENERAL PUBLIC LICENSE |
|
59 | GNU GENERAL PUBLIC LICENSE | |
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION |
|
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |
61 |
|
61 | |||
62 | 0. This License applies to any program or other work which contains |
|
62 | 0. This License applies to any program or other work which contains | |
63 | a notice placed by the copyright holder saying it may be distributed |
|
63 | a notice placed by the copyright holder saying it may be distributed | |
64 | under the terms of this General Public License. The "Program", below, |
|
64 | under the terms of this General Public License. The "Program", below, | |
65 | refers to any such program or work, and a "work based on the Program" |
|
65 | refers to any such program or work, and a "work based on the Program" | |
66 | means either the Program or any derivative work under copyright law: |
|
66 | means either the Program or any derivative work under copyright law: | |
67 | that is to say, a work containing the Program or a portion of it, |
|
67 | that is to say, a work containing the Program or a portion of it, | |
68 | either verbatim or with modifications and/or translated into another |
|
68 | either verbatim or with modifications and/or translated into another | |
69 | language. (Hereinafter, translation is included without limitation in |
|
69 | language. (Hereinafter, translation is included without limitation in | |
70 | the term "modification".) Each licensee is addressed as "you". |
|
70 | the term "modification".) Each licensee is addressed as "you". | |
71 |
|
71 | |||
72 | Activities other than copying, distribution and modification are not |
|
72 | Activities other than copying, distribution and modification are not | |
73 | covered by this License; they are outside its scope. The act of |
|
73 | covered by this License; they are outside its scope. The act of | |
74 | running the Program is not restricted, and the output from the Program |
|
74 | running the Program is not restricted, and the output from the Program | |
75 | is covered only if its contents constitute a work based on the |
|
75 | is covered only if its contents constitute a work based on the | |
76 | Program (independent of having been made by running the Program). |
|
76 | Program (independent of having been made by running the Program). | |
77 | Whether that is true depends on what the Program does. |
|
77 | Whether that is true depends on what the Program does. | |
78 |
|
78 | |||
79 | 1. You may copy and distribute verbatim copies of the Program's |
|
79 | 1. You may copy and distribute verbatim copies of the Program's | |
80 | source code as you receive it, in any medium, provided that you |
|
80 | source code as you receive it, in any medium, provided that you | |
81 | conspicuously and appropriately publish on each copy an appropriate |
|
81 | conspicuously and appropriately publish on each copy an appropriate | |
82 | copyright notice and disclaimer of warranty; keep intact all the |
|
82 | copyright notice and disclaimer of warranty; keep intact all the | |
83 | notices that refer to this License and to the absence of any warranty; |
|
83 | notices that refer to this License and to the absence of any warranty; | |
84 | and give any other recipients of the Program a copy of this License |
|
84 | and give any other recipients of the Program a copy of this License | |
85 | along with the Program. |
|
85 | along with the Program. | |
86 |
|
86 | |||
87 | You may charge a fee for the physical act of transferring a copy, and |
|
87 | You may charge a fee for the physical act of transferring a copy, and | |
88 | you may at your option offer warranty protection in exchange for a fee. |
|
88 | you may at your option offer warranty protection in exchange for a fee. | |
89 |
|
89 | |||
90 | 2. You may modify your copy or copies of the Program or any portion |
|
90 | 2. You may modify your copy or copies of the Program or any portion | |
91 | of it, thus forming a work based on the Program, and copy and |
|
91 | of it, thus forming a work based on the Program, and copy and | |
92 | distribute such modifications or work under the terms of Section 1 |
|
92 | distribute such modifications or work under the terms of Section 1 | |
93 | above, provided that you also meet all of these conditions: |
|
93 | above, provided that you also meet all of these conditions: | |
94 |
|
94 | |||
95 | a) You must cause the modified files to carry prominent notices |
|
95 | a) You must cause the modified files to carry prominent notices | |
96 | stating that you changed the files and the date of any change. |
|
96 | stating that you changed the files and the date of any change. | |
97 |
|
97 | |||
98 | b) You must cause any work that you distribute or publish, that in |
|
98 | b) You must cause any work that you distribute or publish, that in | |
99 | whole or in part contains or is derived from the Program or any |
|
99 | whole or in part contains or is derived from the Program or any | |
100 | part thereof, to be licensed as a whole at no charge to all third |
|
100 | part thereof, to be licensed as a whole at no charge to all third | |
101 | parties under the terms of this License. |
|
101 | parties under the terms of this License. | |
102 |
|
102 | |||
103 | c) If the modified program normally reads commands interactively |
|
103 | c) If the modified program normally reads commands interactively | |
104 | when run, you must cause it, when started running for such |
|
104 | when run, you must cause it, when started running for such | |
105 | interactive use in the most ordinary way, to print or display an |
|
105 | interactive use in the most ordinary way, to print or display an | |
106 | announcement including an appropriate copyright notice and a |
|
106 | announcement including an appropriate copyright notice and a | |
107 | notice that there is no warranty (or else, saying that you provide |
|
107 | notice that there is no warranty (or else, saying that you provide | |
108 | a warranty) and that users may redistribute the program under |
|
108 | a warranty) and that users may redistribute the program under | |
109 | these conditions, and telling the user how to view a copy of this |
|
109 | these conditions, and telling the user how to view a copy of this | |
110 | License. (Exception: if the Program itself is interactive but |
|
110 | License. (Exception: if the Program itself is interactive but | |
111 | does not normally print such an announcement, your work based on |
|
111 | does not normally print such an announcement, your work based on | |
112 | the Program is not required to print an announcement.) |
|
112 | the Program is not required to print an announcement.) | |
113 |
|
113 | |||
114 | These requirements apply to the modified work as a whole. If |
|
114 | These requirements apply to the modified work as a whole. If | |
115 | identifiable sections of that work are not derived from the Program, |
|
115 | identifiable sections of that work are not derived from the Program, | |
116 | and can be reasonably considered independent and separate works in |
|
116 | and can be reasonably considered independent and separate works in | |
117 | themselves, then this License, and its terms, do not apply to those |
|
117 | themselves, then this License, and its terms, do not apply to those | |
118 | sections when you distribute them as separate works. But when you |
|
118 | sections when you distribute them as separate works. But when you | |
119 | distribute the same sections as part of a whole which is a work based |
|
119 | distribute the same sections as part of a whole which is a work based | |
120 | on the Program, the distribution of the whole must be on the terms of |
|
120 | on the Program, the distribution of the whole must be on the terms of | |
121 | this License, whose permissions for other licensees extend to the |
|
121 | this License, whose permissions for other licensees extend to the | |
122 | entire whole, and thus to each and every part regardless of who wrote it. |
|
122 | entire whole, and thus to each and every part regardless of who wrote it. | |
123 |
|
123 | |||
124 | Thus, it is not the intent of this section to claim rights or contest |
|
124 | Thus, it is not the intent of this section to claim rights or contest | |
125 | your rights to work written entirely by you; rather, the intent is to |
|
125 | your rights to work written entirely by you; rather, the intent is to | |
126 | exercise the right to control the distribution of derivative or |
|
126 | exercise the right to control the distribution of derivative or | |
127 | collective works based on the Program. |
|
127 | collective works based on the Program. | |
128 |
|
128 | |||
129 | In addition, mere aggregation of another work not based on the Program |
|
129 | In addition, mere aggregation of another work not based on the Program | |
130 | with the Program (or with a work based on the Program) on a volume of |
|
130 | with the Program (or with a work based on the Program) on a volume of | |
131 | a storage or distribution medium does not bring the other work under |
|
131 | a storage or distribution medium does not bring the other work under | |
132 | the scope of this License. |
|
132 | the scope of this License. | |
133 |
|
133 | |||
134 | 3. You may copy and distribute the Program (or a work based on it, |
|
134 | 3. You may copy and distribute the Program (or a work based on it, | |
135 | under Section 2) in object code or executable form under the terms of |
|
135 | under Section 2) in object code or executable form under the terms of | |
136 | Sections 1 and 2 above provided that you also do one of the following: |
|
136 | Sections 1 and 2 above provided that you also do one of the following: | |
137 |
|
137 | |||
138 | a) Accompany it with the complete corresponding machine-readable |
|
138 | a) Accompany it with the complete corresponding machine-readable | |
139 | source code, which must be distributed under the terms of Sections |
|
139 | source code, which must be distributed under the terms of Sections | |
140 | 1 and 2 above on a medium customarily used for software interchange; or, |
|
140 | 1 and 2 above on a medium customarily used for software interchange; or, | |
141 |
|
141 | |||
142 | b) Accompany it with a written offer, valid for at least three |
|
142 | b) Accompany it with a written offer, valid for at least three | |
143 | years, to give any third party, for a charge no more than your |
|
143 | years, to give any third party, for a charge no more than your | |
144 | cost of physically performing source distribution, a complete |
|
144 | cost of physically performing source distribution, a complete | |
145 | machine-readable copy of the corresponding source code, to be |
|
145 | machine-readable copy of the corresponding source code, to be | |
146 | distributed under the terms of Sections 1 and 2 above on a medium |
|
146 | distributed under the terms of Sections 1 and 2 above on a medium | |
147 | customarily used for software interchange; or, |
|
147 | customarily used for software interchange; or, | |
148 |
|
148 | |||
149 | c) Accompany it with the information you received as to the offer |
|
149 | c) Accompany it with the information you received as to the offer | |
150 | to distribute corresponding source code. (This alternative is |
|
150 | to distribute corresponding source code. (This alternative is | |
151 | allowed only for noncommercial distribution and only if you |
|
151 | allowed only for noncommercial distribution and only if you | |
152 | received the program in object code or executable form with such |
|
152 | received the program in object code or executable form with such | |
153 | an offer, in accord with Subsection b above.) |
|
153 | an offer, in accord with Subsection b above.) | |
154 |
|
154 | |||
155 | The source code for a work means the preferred form of the work for |
|
155 | The source code for a work means the preferred form of the work for | |
156 | making modifications to it. For an executable work, complete source |
|
156 | making modifications to it. For an executable work, complete source | |
157 | code means all the source code for all modules it contains, plus any |
|
157 | code means all the source code for all modules it contains, plus any | |
158 | associated interface definition files, plus the scripts used to |
|
158 | associated interface definition files, plus the scripts used to | |
159 | control compilation and installation of the executable. However, as a |
|
159 | control compilation and installation of the executable. However, as a | |
160 | special exception, the source code distributed need not include |
|
160 | special exception, the source code distributed need not include | |
161 | anything that is normally distributed (in either source or binary |
|
161 | anything that is normally distributed (in either source or binary | |
162 | form) with the major components (compiler, kernel, and so on) of the |
|
162 | form) with the major components (compiler, kernel, and so on) of the | |
163 | operating system on which the executable runs, unless that component |
|
163 | operating system on which the executable runs, unless that component | |
164 | itself accompanies the executable. |
|
164 | itself accompanies the executable. | |
165 |
|
165 | |||
166 | If distribution of executable or object code is made by offering |
|
166 | If distribution of executable or object code is made by offering | |
167 | access to copy from a designated place, then offering equivalent |
|
167 | access to copy from a designated place, then offering equivalent | |
168 | access to copy the source code from the same place counts as |
|
168 | access to copy the source code from the same place counts as | |
169 | distribution of the source code, even though third parties are not |
|
169 | distribution of the source code, even though third parties are not | |
170 | compelled to copy the source along with the object code. |
|
170 | compelled to copy the source along with the object code. | |
171 |
|
171 | |||
172 | 4. You may not copy, modify, sublicense, or distribute the Program |
|
172 | 4. You may not copy, modify, sublicense, or distribute the Program | |
173 | except as expressly provided under this License. Any attempt |
|
173 | except as expressly provided under this License. Any attempt | |
174 | otherwise to copy, modify, sublicense or distribute the Program is |
|
174 | otherwise to copy, modify, sublicense or distribute the Program is | |
175 | void, and will automatically terminate your rights under this License. |
|
175 | void, and will automatically terminate your rights under this License. | |
176 | However, parties who have received copies, or rights, from you under |
|
176 | However, parties who have received copies, or rights, from you under | |
177 | this License will not have their licenses terminated so long as such |
|
177 | this License will not have their licenses terminated so long as such | |
178 | parties remain in full compliance. |
|
178 | parties remain in full compliance. | |
179 |
|
179 | |||
180 | 5. You are not required to accept this License, since you have not |
|
180 | 5. You are not required to accept this License, since you have not | |
181 | signed it. However, nothing else grants you permission to modify or |
|
181 | signed it. However, nothing else grants you permission to modify or | |
182 | distribute the Program or its derivative works. These actions are |
|
182 | distribute the Program or its derivative works. These actions are | |
183 | prohibited by law if you do not accept this License. Therefore, by |
|
183 | prohibited by law if you do not accept this License. Therefore, by | |
184 | modifying or distributing the Program (or any work based on the |
|
184 | modifying or distributing the Program (or any work based on the | |
185 | Program), you indicate your acceptance of this License to do so, and |
|
185 | Program), you indicate your acceptance of this License to do so, and | |
186 | all its terms and conditions for copying, distributing or modifying |
|
186 | all its terms and conditions for copying, distributing or modifying | |
187 | the Program or works based on it. |
|
187 | the Program or works based on it. | |
188 |
|
188 | |||
189 | 6. Each time you redistribute the Program (or any work based on the |
|
189 | 6. Each time you redistribute the Program (or any work based on the | |
190 | Program), the recipient automatically receives a license from the |
|
190 | Program), the recipient automatically receives a license from the | |
191 | original licensor to copy, distribute or modify the Program subject to |
|
191 | original licensor to copy, distribute or modify the Program subject to | |
192 | these terms and conditions. You may not impose any further |
|
192 | these terms and conditions. You may not impose any further | |
193 | restrictions on the recipients' exercise of the rights granted herein. |
|
193 | restrictions on the recipients' exercise of the rights granted herein. | |
194 | You are not responsible for enforcing compliance by third parties to |
|
194 | You are not responsible for enforcing compliance by third parties to | |
195 | this License. |
|
195 | this License. | |
196 |
|
196 | |||
197 | 7. If, as a consequence of a court judgment or allegation of patent |
|
197 | 7. If, as a consequence of a court judgment or allegation of patent | |
198 | infringement or for any other reason (not limited to patent issues), |
|
198 | infringement or for any other reason (not limited to patent issues), | |
199 | conditions are imposed on you (whether by court order, agreement or |
|
199 | conditions are imposed on you (whether by court order, agreement or | |
200 | otherwise) that contradict the conditions of this License, they do not |
|
200 | otherwise) that contradict the conditions of this License, they do not | |
201 | excuse you from the conditions of this License. If you cannot |
|
201 | excuse you from the conditions of this License. If you cannot | |
202 | distribute so as to satisfy simultaneously your obligations under this |
|
202 | distribute so as to satisfy simultaneously your obligations under this | |
203 | License and any other pertinent obligations, then as a consequence you |
|
203 | License and any other pertinent obligations, then as a consequence you | |
204 | may not distribute the Program at all. For example, if a patent |
|
204 | may not distribute the Program at all. For example, if a patent | |
205 | license would not permit royalty-free redistribution of the Program by |
|
205 | license would not permit royalty-free redistribution of the Program by | |
206 | all those who receive copies directly or indirectly through you, then |
|
206 | all those who receive copies directly or indirectly through you, then | |
207 | the only way you could satisfy both it and this License would be to |
|
207 | the only way you could satisfy both it and this License would be to | |
208 | refrain entirely from distribution of the Program. |
|
208 | refrain entirely from distribution of the Program. | |
209 |
|
209 | |||
210 | If any portion of this section is held invalid or unenforceable under |
|
210 | If any portion of this section is held invalid or unenforceable under | |
211 | any particular circumstance, the balance of the section is intended to |
|
211 | any particular circumstance, the balance of the section is intended to | |
212 | apply and the section as a whole is intended to apply in other |
|
212 | apply and the section as a whole is intended to apply in other | |
213 | circumstances. |
|
213 | circumstances. | |
214 |
|
214 | |||
215 | It is not the purpose of this section to induce you to infringe any |
|
215 | It is not the purpose of this section to induce you to infringe any | |
216 | patents or other property right claims or to contest validity of any |
|
216 | patents or other property right claims or to contest validity of any | |
217 | such claims; this section has the sole purpose of protecting the |
|
217 | such claims; this section has the sole purpose of protecting the | |
218 | integrity of the free software distribution system, which is |
|
218 | integrity of the free software distribution system, which is | |
219 | implemented by public license practices. Many people have made |
|
219 | implemented by public license practices. Many people have made | |
220 | generous contributions to the wide range of software distributed |
|
220 | generous contributions to the wide range of software distributed | |
221 | through that system in reliance on consistent application of that |
|
221 | through that system in reliance on consistent application of that | |
222 | system; it is up to the author/donor to decide if he or she is willing |
|
222 | system; it is up to the author/donor to decide if he or she is willing | |
223 | to distribute software through any other system and a licensee cannot |
|
223 | to distribute software through any other system and a licensee cannot | |
224 | impose that choice. |
|
224 | impose that choice. | |
225 |
|
225 | |||
226 | This section is intended to make thoroughly clear what is believed to |
|
226 | This section is intended to make thoroughly clear what is believed to | |
227 | be a consequence of the rest of this License. |
|
227 | be a consequence of the rest of this License. | |
228 |
|
228 | |||
229 | 8. If the distribution and/or use of the Program is restricted in |
|
229 | 8. If the distribution and/or use of the Program is restricted in | |
230 | certain countries either by patents or by copyrighted interfaces, the |
|
230 | certain countries either by patents or by copyrighted interfaces, the | |
231 | original copyright holder who places the Program under this License |
|
231 | original copyright holder who places the Program under this License | |
232 | may add an explicit geographical distribution limitation excluding |
|
232 | may add an explicit geographical distribution limitation excluding | |
233 | those countries, so that distribution is permitted only in or among |
|
233 | those countries, so that distribution is permitted only in or among | |
234 | countries not thus excluded. In such case, this License incorporates |
|
234 | countries not thus excluded. In such case, this License incorporates | |
235 | the limitation as if written in the body of this License. |
|
235 | the limitation as if written in the body of this License. | |
236 |
|
236 | |||
237 | 9. The Free Software Foundation may publish revised and/or new versions |
|
237 | 9. The Free Software Foundation may publish revised and/or new versions | |
238 | of the General Public License from time to time. Such new versions will |
|
238 | of the General Public License from time to time. Such new versions will | |
239 | be similar in spirit to the present version, but may differ in detail to |
|
239 | be similar in spirit to the present version, but may differ in detail to | |
240 | address new problems or concerns. |
|
240 | address new problems or concerns. | |
241 |
|
241 | |||
242 | Each version is given a distinguishing version number. If the Program |
|
242 | Each version is given a distinguishing version number. If the Program | |
243 | specifies a version number of this License which applies to it and "any |
|
243 | specifies a version number of this License which applies to it and "any | |
244 | later version", you have the option of following the terms and conditions |
|
244 | later version", you have the option of following the terms and conditions | |
245 | either of that version or of any later version published by the Free |
|
245 | either of that version or of any later version published by the Free | |
246 | Software Foundation. If the Program does not specify a version number of |
|
246 | Software Foundation. If the Program does not specify a version number of | |
247 | this License, you may choose any version ever published by the Free Software |
|
247 | this License, you may choose any version ever published by the Free Software | |
248 | Foundation. |
|
248 | Foundation. | |
249 |
|
249 | |||
250 | 10. If you wish to incorporate parts of the Program into other free |
|
250 | 10. If you wish to incorporate parts of the Program into other free | |
251 | programs whose distribution conditions are different, write to the author |
|
251 | programs whose distribution conditions are different, write to the author | |
252 | to ask for permission. For software which is copyrighted by the Free |
|
252 | to ask for permission. For software which is copyrighted by the Free | |
253 | Software Foundation, write to the Free Software Foundation; we sometimes |
|
253 | Software Foundation, write to the Free Software Foundation; we sometimes | |
254 | make exceptions for this. Our decision will be guided by the two goals |
|
254 | make exceptions for this. Our decision will be guided by the two goals | |
255 | of preserving the free status of all derivatives of our free software and |
|
255 | of preserving the free status of all derivatives of our free software and | |
256 | of promoting the sharing and reuse of software generally. |
|
256 | of promoting the sharing and reuse of software generally. | |
257 |
|
257 | |||
258 | NO WARRANTY |
|
258 | NO WARRANTY | |
259 |
|
259 | |||
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY |
|
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | |
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN |
|
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | |
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES |
|
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | |
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED |
|
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | |
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
|
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS |
|
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS | |
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE |
|
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE | |
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, |
|
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | |
268 | REPAIR OR CORRECTION. |
|
268 | REPAIR OR CORRECTION. | |
269 |
|
269 | |||
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING |
|
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | |
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR |
|
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | |
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, |
|
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | |
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING |
|
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | |
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED |
|
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | |
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY |
|
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | |
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER |
|
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | |
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE |
|
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | |
278 | POSSIBILITY OF SUCH DAMAGES. |
|
278 | POSSIBILITY OF SUCH DAMAGES. | |
279 |
|
279 | |||
280 | END OF TERMS AND CONDITIONS |
|
280 | END OF TERMS AND CONDITIONS | |
281 |
|
281 | |||
282 | How to Apply These Terms to Your New Programs |
|
282 | How to Apply These Terms to Your New Programs | |
283 |
|
283 | |||
284 | If you develop a new program, and you want it to be of the greatest |
|
284 | If you develop a new program, and you want it to be of the greatest | |
285 | possible use to the public, the best way to achieve this is to make it |
|
285 | possible use to the public, the best way to achieve this is to make it | |
286 | free software which everyone can redistribute and change under these terms. |
|
286 | free software which everyone can redistribute and change under these terms. | |
287 |
|
287 | |||
288 | To do so, attach the following notices to the program. It is safest |
|
288 | To do so, attach the following notices to the program. It is safest | |
289 | to attach them to the start of each source file to most effectively |
|
289 | to attach them to the start of each source file to most effectively | |
290 | convey the exclusion of warranty; and each file should have at least |
|
290 | convey the exclusion of warranty; and each file should have at least | |
291 | the "copyright" line and a pointer to where the full notice is found. |
|
291 | the "copyright" line and a pointer to where the full notice is found. | |
292 |
|
292 | |||
293 | <one line to give the program's name and a brief idea of what it does.> |
|
293 | <one line to give the program's name and a brief idea of what it does.> | |
294 | Copyright (C) <year> <name of author> |
|
294 | Copyright (C) <year> <name of author> | |
295 |
|
295 | |||
296 | This program is free software; you can redistribute it and/or modify |
|
296 | This program is free software; you can redistribute it and/or modify | |
297 | it under the terms of the GNU General Public License as published by |
|
297 | it under the terms of the GNU General Public License as published by | |
298 | the Free Software Foundation; either version 2 of the License, or |
|
298 | the Free Software Foundation; either version 2 of the License, or | |
299 | (at your option) any later version. |
|
299 | (at your option) any later version. | |
300 |
|
300 | |||
301 | This program is distributed in the hope that it will be useful, |
|
301 | This program is distributed in the hope that it will be useful, | |
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
304 | GNU General Public License for more details. |
|
304 | GNU General Public License for more details. | |
305 |
|
305 | |||
306 | You should have received a copy of the GNU General Public License |
|
306 | You should have received a copy of the GNU General Public License | |
307 | along with this program; if not, write to the Free Software |
|
307 | along with this program; if not, write to the Free Software | |
308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
309 |
|
309 | |||
310 |
|
310 | |||
311 | Also add information on how to contact you by electronic and paper mail. |
|
311 | Also add information on how to contact you by electronic and paper mail. | |
312 |
|
312 | |||
313 | If the program is interactive, make it output a short notice like this |
|
313 | If the program is interactive, make it output a short notice like this | |
314 | when it starts in an interactive mode: |
|
314 | when it starts in an interactive mode: | |
315 |
|
315 | |||
316 | Gnomovision version 69, Copyright (C) year name of author |
|
316 | Gnomovision version 69, Copyright (C) year name of author | |
317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. |
|
317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | |
318 | This is free software, and you are welcome to redistribute it |
|
318 | This is free software, and you are welcome to redistribute it | |
319 | under certain conditions; type `show c' for details. |
|
319 | under certain conditions; type `show c' for details. | |
320 |
|
320 | |||
321 | The hypothetical commands `show w' and `show c' should show the appropriate |
|
321 | The hypothetical commands `show w' and `show c' should show the appropriate | |
322 | parts of the General Public License. Of course, the commands you use may |
|
322 | parts of the General Public License. Of course, the commands you use may | |
323 | be called something other than `show w' and `show c'; they could even be |
|
323 | be called something other than `show w' and `show c'; they could even be | |
324 | mouse-clicks or menu items--whatever suits your program. |
|
324 | mouse-clicks or menu items--whatever suits your program. | |
325 |
|
325 | |||
326 | You should also get your employer (if you work as a programmer) or your |
|
326 | You should also get your employer (if you work as a programmer) or your | |
327 | school, if any, to sign a "copyright disclaimer" for the program, if |
|
327 | school, if any, to sign a "copyright disclaimer" for the program, if | |
328 | necessary. Here is a sample; alter the names: |
|
328 | necessary. Here is a sample; alter the names: | |
329 |
|
329 | |||
330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program |
|
330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program | |
331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. |
|
331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. | |
332 |
|
332 | |||
333 | <signature of Ty Coon>, 1 April 1989 |
|
333 | <signature of Ty Coon>, 1 April 1989 | |
334 | Ty Coon, President of Vice |
|
334 | Ty Coon, President of Vice | |
335 |
|
335 | |||
336 | This General Public License does not permit incorporating your program into |
|
336 | This General Public License does not permit incorporating your program into | |
337 | proprietary programs. If your program is a subroutine library, you may |
|
337 | proprietary programs. If your program is a subroutine library, you may | |
338 | consider it more useful to permit linking proprietary applications with the |
|
338 | consider it more useful to permit linking proprietary applications with the | |
339 | library. If this is what you want to do, use the GNU Library General |
|
339 | library. If this is what you want to do, use the GNU Library General | |
340 | Public License instead of this License. |
|
340 | Public License instead of this License. |
@@ -1,13 +1,35 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # Hg app, a web based mercurial repository managment based on pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | ||||
1 | """ |
|
21 | """ | |
|
22 | Created on April 9, 2010 | |||
2 | Hg app, a web based mercurial repository managment based on pylons |
|
23 | Hg app, a web based mercurial repository managment based on pylons | |
|
24 | @author: marcink | |||
3 | """ |
|
25 | """ | |
4 |
|
26 | |||
5 | VERSION = (0, 7, 6, 'beta') |
|
27 | VERSION = (0, 7, 6, 'beta') | |
6 |
|
28 | |||
7 | __version__ = '.'.join((str(each) for each in VERSION[:4])) |
|
29 | __version__ = '.'.join((str(each) for each in VERSION[:4])) | |
8 |
|
30 | |||
9 | def get_version(): |
|
31 | def get_version(): | |
10 | """ |
|
32 | """ | |
11 | Returns shorter version (digit parts only) as string. |
|
33 | Returns shorter version (digit parts only) as string. | |
12 | """ |
|
34 | """ | |
13 | return '.'.join((str(each) for each in VERSION[:3])) |
|
35 | return '.'.join((str(each) for each in VERSION[:3])) |
@@ -1,70 +1,68 b'' | |||||
1 | """Pylons environment configuration""" |
|
1 | """Pylons environment configuration""" | |
2 | from mako.lookup import TemplateLookup |
|
2 | from mako.lookup import TemplateLookup | |
3 | from pylons.configuration import PylonsConfig |
|
3 | from pylons.configuration import PylonsConfig | |
4 | from pylons.error import handle_mako_error |
|
4 | from pylons.error import handle_mako_error | |
5 | from pylons_app.config.routing import make_map |
|
5 | from pylons_app.config.routing import make_map | |
6 | from pylons_app.lib.auth import set_available_permissions |
|
6 | from pylons_app.lib.auth import set_available_permissions | |
|
7 | from pylons_app.lib.utils import repo2db_mapper | |||
7 | from pylons_app.model import init_model |
|
8 | from pylons_app.model import init_model | |
8 | from sqlalchemy import engine_from_config |
|
9 | from sqlalchemy import engine_from_config | |
9 | import logging |
|
10 | import logging | |
10 | import os |
|
11 | import os | |
11 | import pylons_app.lib.app_globals as app_globals |
|
12 | import pylons_app.lib.app_globals as app_globals | |
12 | import pylons_app.lib.helpers |
|
13 | import pylons_app.lib.helpers | |
13 |
|
14 | |||
14 |
|
||||
15 |
|
||||
16 | log = logging.getLogger(__name__) |
|
15 | log = logging.getLogger(__name__) | |
17 |
|
16 | |||
18 | def load_environment(global_conf, app_conf): |
|
17 | def load_environment(global_conf, app_conf): | |
19 | """Configure the Pylons environment via the ``pylons.config`` |
|
18 | """Configure the Pylons environment via the ``pylons.config`` | |
20 | object |
|
19 | object | |
21 | """ |
|
20 | """ | |
22 | config = PylonsConfig() |
|
21 | config = PylonsConfig() | |
23 |
|
22 | |||
24 | # Pylons paths |
|
23 | # Pylons paths | |
25 | root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) |
|
24 | root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
26 | paths = dict(root=root, |
|
25 | paths = dict(root=root, | |
27 | controllers=os.path.join(root, 'controllers'), |
|
26 | controllers=os.path.join(root, 'controllers'), | |
28 | static_files=os.path.join(root, 'public'), |
|
27 | static_files=os.path.join(root, 'public'), | |
29 | templates=[os.path.join(root, 'templates')]) |
|
28 | templates=[os.path.join(root, 'templates')]) | |
30 |
|
29 | |||
31 | # Initialize config with the basic options |
|
30 | # Initialize config with the basic options | |
32 | config.init_app(global_conf, app_conf, package='pylons_app', paths=paths) |
|
31 | config.init_app(global_conf, app_conf, package='pylons_app', paths=paths) | |
33 |
|
32 | |||
34 | config['routes.map'] = make_map(config) |
|
33 | config['routes.map'] = make_map(config) | |
35 | config['pylons.app_globals'] = app_globals.Globals(config) |
|
34 | config['pylons.app_globals'] = app_globals.Globals(config) | |
36 | config['pylons.h'] = pylons_app.lib.helpers |
|
35 | config['pylons.h'] = pylons_app.lib.helpers | |
37 |
|
36 | |||
38 | # Setup cache object as early as possible |
|
37 | # Setup cache object as early as possible | |
39 | import pylons |
|
38 | import pylons | |
40 | pylons.cache._push_object(config['pylons.app_globals'].cache) |
|
39 | pylons.cache._push_object(config['pylons.app_globals'].cache) | |
41 |
|
40 | |||
42 |
|
||||
43 | # Create the Mako TemplateLookup, with the default auto-escaping |
|
41 | # Create the Mako TemplateLookup, with the default auto-escaping | |
44 | config['pylons.app_globals'].mako_lookup = TemplateLookup( |
|
42 | config['pylons.app_globals'].mako_lookup = TemplateLookup( | |
45 | directories=paths['templates'], |
|
43 | directories=paths['templates'], | |
46 | error_handler=handle_mako_error, |
|
44 | error_handler=handle_mako_error, | |
47 | module_directory=os.path.join(app_conf['cache_dir'], 'templates'), |
|
45 | module_directory=os.path.join(app_conf['cache_dir'], 'templates'), | |
48 | input_encoding='utf-8', default_filters=['escape'], |
|
46 | input_encoding='utf-8', default_filters=['escape'], | |
49 | imports=['from webhelpers.html import escape']) |
|
47 | imports=['from webhelpers.html import escape']) | |
50 |
|
48 | |||
51 | #sets the c attribute access when don't existing attribute ar accessed |
|
49 | #sets the c attribute access when don't existing attribute are accessed | |
52 | config['pylons.strict_tmpl_context'] = True |
|
50 | config['pylons.strict_tmpl_context'] = True | |
53 |
|
51 | |||
54 | #MULTIPLE DB configs |
|
52 | #MULTIPLE DB configs | |
55 | # Setup the SQLAlchemy database engine |
|
53 | # Setup the SQLAlchemy database engine | |
56 | if config['debug']: |
|
54 | if config['debug']: | |
57 | #use query time debugging. |
|
55 | #use query time debugging. | |
58 | from pylons_app.lib.timerproxy import TimerProxy |
|
56 | from pylons_app.lib.timerproxy import TimerProxy | |
59 | sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.', |
|
57 | sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.', | |
60 | proxy=TimerProxy()) |
|
58 | proxy=TimerProxy()) | |
61 | else: |
|
59 | else: | |
62 | sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.') |
|
60 | sa_engine_db1 = engine_from_config(config, 'sqlalchemy.db1.') | |
63 |
|
61 | |||
64 | init_model(sa_engine_db1) |
|
62 | init_model(sa_engine_db1) | |
65 |
|
63 | repo2db_mapper() | ||
66 | set_available_permissions(config) |
|
64 | set_available_permissions(config) | |
67 | # CONFIGURATION OPTIONS HERE (note: all config options will override |
|
65 | # CONFIGURATION OPTIONS HERE (note: all config options will override | |
68 | # any Pylons config options) |
|
66 | # any Pylons config options) | |
69 |
|
67 | |||
70 | return config |
|
68 | return config |
@@ -1,31 +1,54 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # admin controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 7, 2010 | |||
|
22 | admin controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
1 | import logging |
|
25 | import logging | |
2 |
from pylons import request, response, session, tmpl_context as c |
|
26 | from pylons import request, response, session, tmpl_context as c | |
3 | from pylons.controllers.util import abort, redirect |
|
|||
4 | from pylons_app.lib.base import BaseController, render |
|
27 | from pylons_app.lib.base import BaseController, render | |
5 | from pylons_app.model import meta |
|
28 | from pylons_app.model import meta | |
6 | from pylons_app.model.db import UserLog |
|
29 | from pylons_app.model.db import UserLog | |
7 | from webhelpers.paginate import Page |
|
30 | from webhelpers.paginate import Page | |
8 | from pylons_app.lib.auth import LoginRequired |
|
31 | from pylons_app.lib.auth import LoginRequired | |
9 |
|
32 | |||
10 | log = logging.getLogger(__name__) |
|
33 | log = logging.getLogger(__name__) | |
11 |
|
34 | |||
12 | class AdminController(BaseController): |
|
35 | class AdminController(BaseController): | |
13 |
|
36 | |||
14 | @LoginRequired() |
|
37 | @LoginRequired() | |
15 | def __before__(self): |
|
38 | def __before__(self): | |
16 | user = session['hg_app_user'] |
|
39 | user = session['hg_app_user'] | |
17 | c.admin_user = user.is_admin |
|
40 | c.admin_user = user.is_admin | |
18 | c.admin_username = user.username |
|
41 | c.admin_username = user.username | |
19 | super(AdminController, self).__before__() |
|
42 | super(AdminController, self).__before__() | |
20 |
|
43 | |||
21 | def index(self): |
|
44 | def index(self): | |
22 | sa = meta.Session |
|
45 | sa = meta.Session | |
23 |
|
46 | |||
24 | users_log = sa.query(UserLog).order_by(UserLog.action_date.desc()) |
|
47 | users_log = sa.query(UserLog).order_by(UserLog.action_date.desc()) | |
25 | p = int(request.params.get('page', 1)) |
|
48 | p = int(request.params.get('page', 1)) | |
26 | c.users_log = Page(users_log, page=p, items_per_page=10) |
|
49 | c.users_log = Page(users_log, page=p, items_per_page=10) | |
27 | c.log_data = render('admin/admin_log.html') |
|
50 | c.log_data = render('admin/admin_log.html') | |
28 | if request.params.get('partial'): |
|
51 | if request.params.get('partial'): | |
29 | return c.log_data |
|
52 | return c.log_data | |
30 | return render('admin/admin.html') |
|
53 | return render('admin/admin.html') | |
31 |
|
54 |
@@ -1,20 +1,44 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # branches controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 21, 2010 | |||
|
22 | branches controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
1 | from pylons import tmpl_context as c, app_globals as g |
|
25 | from pylons import tmpl_context as c, app_globals as g | |
2 | from pylons_app.lib.auth import LoginRequired |
|
26 | from pylons_app.lib.auth import LoginRequired | |
3 | from pylons_app.lib.base import BaseController, render |
|
27 | from pylons_app.lib.base import BaseController, render | |
4 | from pylons_app.model.hg_model import HgModel |
|
28 | from pylons_app.model.hg_model import HgModel | |
5 | import logging |
|
29 | import logging | |
6 |
|
30 | |||
7 | log = logging.getLogger(__name__) |
|
31 | log = logging.getLogger(__name__) | |
8 |
|
32 | |||
9 | class BranchesController(BaseController): |
|
33 | class BranchesController(BaseController): | |
10 |
|
34 | |||
11 | @LoginRequired() |
|
35 | @LoginRequired() | |
12 | def __before__(self): |
|
36 | def __before__(self): | |
13 | super(BranchesController, self).__before__() |
|
37 | super(BranchesController, self).__before__() | |
14 |
|
38 | |||
15 | def index(self): |
|
39 | def index(self): | |
16 | hg_model = HgModel() |
|
40 | hg_model = HgModel() | |
17 | c.repo_info = hg_model.get_repo(c.repo_name) |
|
41 | c.repo_info = hg_model.get_repo(c.repo_name) | |
18 | c.repo_branches = c.repo_info.branches |
|
42 | c.repo_branches = c.repo_info.branches | |
19 |
|
43 | |||
20 | return render('branches/branches.html') |
|
44 | return render('branches/branches.html') |
@@ -1,74 +1,95 b'' | |||||
1 | from mercurial.graphmod import revisions as graph_rev, colored, CHANGESET |
|
1 | #!/usr/bin/env python | |
2 | from mercurial.node import short |
|
2 | # encoding: utf-8 | |
|
3 | # changelog controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 21, 2010 | |||
|
22 | changelog controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
3 | from pylons import request, session, tmpl_context as c |
|
25 | from pylons import request, session, tmpl_context as c | |
4 | from pylons_app.lib.auth import LoginRequired |
|
26 | from pylons_app.lib.auth import LoginRequired | |
5 | from pylons_app.lib.base import BaseController, render |
|
27 | from pylons_app.lib.base import BaseController, render | |
6 | from pylons_app.lib.filters import age as _age, person |
|
|||
7 | from pylons_app.model.hg_model import _full_changelog_cached |
|
28 | from pylons_app.model.hg_model import _full_changelog_cached | |
8 | from simplejson import dumps |
|
|||
9 | from webhelpers.paginate import Page |
|
29 | from webhelpers.paginate import Page | |
10 | import logging |
|
30 | import logging | |
11 | log = logging.getLogger(__name__) |
|
31 | log = logging.getLogger(__name__) | |
12 |
|
32 | |||
13 | class ChangelogController(BaseController): |
|
33 | class ChangelogController(BaseController): | |
14 |
|
34 | |||
15 | @LoginRequired() |
|
35 | @LoginRequired() | |
16 | def __before__(self): |
|
36 | def __before__(self): | |
17 | super(ChangelogController, self).__before__() |
|
37 | super(ChangelogController, self).__before__() | |
18 |
|
38 | |||
19 | def index(self): |
|
39 | def index(self): | |
20 | limit = 100 |
|
40 | limit = 100 | |
21 | default = 20 |
|
41 | default = 20 | |
22 | if request.params.get('size'): |
|
42 | if request.params.get('size'): | |
23 | try: |
|
43 | try: | |
24 | int_size = int(request.params.get('size')) |
|
44 | int_size = int(request.params.get('size')) | |
25 | except ValueError: |
|
45 | except ValueError: | |
26 | int_size = default |
|
46 | int_size = default | |
27 | int_size = int_size if int_size <= limit else limit |
|
47 | int_size = int_size if int_size <= limit else limit | |
28 | c.size = int_size |
|
48 | c.size = int_size | |
29 | session['changelog_size'] = c.size |
|
49 | session['changelog_size'] = c.size | |
30 | session.save() |
|
50 | session.save() | |
31 | else: |
|
51 | else: | |
32 | c.size = session.get('changelog_size', default) |
|
52 | c.size = session.get('changelog_size', default) | |
33 |
|
53 | |||
34 | changesets = _full_changelog_cached(c.repo_name) |
|
54 | changesets = _full_changelog_cached(c.repo_name) | |
35 |
|
55 | |||
36 | p = int(request.params.get('page', 1)) |
|
56 | p = int(request.params.get('page', 1)) | |
37 | c.pagination = Page(changesets, page=p, item_count=len(changesets), |
|
57 | c.pagination = Page(changesets, page=p, item_count=len(changesets), | |
38 | items_per_page=c.size) |
|
58 | items_per_page=c.size) | |
39 |
|
59 | |||
40 | #self._graph(c.repo, c.size,p) |
|
60 | #self._graph(c.repo, c.size,p) | |
41 |
|
61 | |||
42 | return render('changelog/changelog.html') |
|
62 | return render('changelog/changelog.html') | |
43 |
|
63 | |||
44 |
|
64 | |||
45 | def _graph(self, repo, size, p): |
|
65 | def _graph(self, repo, size, p): | |
46 | revcount = size |
|
66 | pass | |
47 | if not repo.revisions:return dumps([]), 0 |
|
67 | # revcount = size | |
48 |
|
68 | # if not repo.revisions:return dumps([]), 0 | ||
49 | max_rev = repo.revisions[-1] |
|
69 | # | |
50 | offset = 1 if p == 1 else ((p - 1) * revcount) |
|
70 | # max_rev = repo.revisions[-1] | |
51 | rev_start = repo.revisions[(-1 * offset)] |
|
71 | # offset = 1 if p == 1 else ((p - 1) * revcount) | |
52 | c.bg_height = 120 |
|
72 | # rev_start = repo.revisions[(-1 * offset)] | |
53 |
|
73 | # c.bg_height = 120 | ||
54 | revcount = min(max_rev, revcount) |
|
74 | # | |
55 |
rev |
|
75 | # revcount = min(max_rev, revcount) | |
56 |
|
|
76 | # rev_end = max(0, rev_start - revcount) | |
57 |
|
77 | # dag = graph_rev(repo.repo, rev_start, rev_end) | ||
58 | c.dag = tree = list(colored(dag)) |
|
78 | # | |
59 | canvasheight = (len(tree) + 1) * c.bg_height - 27 |
|
79 | # c.dag = tree = list(colored(dag)) | |
60 | data = [] |
|
80 | # canvasheight = (len(tree) + 1) * c.bg_height - 27 | |
61 | for (id, type, ctx, vtx, edges) in tree: |
|
81 | # data = [] | |
62 | if type != CHANGESET: |
|
82 | # for (id, type, ctx, vtx, edges) in tree: | |
63 | continue |
|
83 | # if type != CHANGESET: | |
64 | node = short(ctx.node()) |
|
84 | # continue | |
65 |
|
|
85 | # node = short(ctx.node()) | |
66 |
|
|
86 | # age = _age(ctx.date()) | |
67 |
|
|
87 | # desc = ctx.description() | |
68 |
|
|
88 | # user = person(ctx.user()) | |
69 | branch = branch, repo.repo.branchtags().get(branch) == ctx.node() |
|
89 | # branch = ctx.branch() | |
70 | data.append((node, vtx, edges, desc, user, age, branch, ctx.tags())) |
|
90 | # branch = branch, repo.repo.branchtags().get(branch) == ctx.node() | |
71 |
|
91 | # data.append((node, vtx, edges, desc, user, age, branch, ctx.tags())) | ||
72 | c.jsdata = dumps(data) |
|
92 | # | |
73 | c.canvasheight = canvasheight |
|
93 | # c.jsdata = dumps(data) | |
|
94 | # c.canvasheight = canvasheight | |||
74 |
|
95 |
@@ -1,40 +1,64 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # changeset controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 25, 2010 | |||
|
22 | changeset controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
1 | from pylons import tmpl_context as c |
|
25 | from pylons import tmpl_context as c | |
2 | from pylons_app.lib.auth import LoginRequired |
|
26 | from pylons_app.lib.auth import LoginRequired | |
3 | from pylons_app.lib.base import BaseController, render |
|
27 | from pylons_app.lib.base import BaseController, render | |
4 | from pylons_app.model.hg_model import HgModel |
|
28 | from pylons_app.model.hg_model import HgModel | |
5 | from vcs.utils import diffs as differ |
|
29 | from vcs.utils import diffs as differ | |
6 | import logging |
|
30 | import logging | |
7 | from vcs.nodes import FileNode |
|
31 | from vcs.nodes import FileNode | |
8 |
|
32 | |||
9 |
|
33 | |||
10 | log = logging.getLogger(__name__) |
|
34 | log = logging.getLogger(__name__) | |
11 |
|
35 | |||
12 | class ChangesetController(BaseController): |
|
36 | class ChangesetController(BaseController): | |
13 |
|
37 | |||
14 | @LoginRequired() |
|
38 | @LoginRequired() | |
15 | def __before__(self): |
|
39 | def __before__(self): | |
16 | super(ChangesetController, self).__before__() |
|
40 | super(ChangesetController, self).__before__() | |
17 |
|
41 | |||
18 | def index(self, revision): |
|
42 | def index(self, revision): | |
19 | hg_model = HgModel() |
|
43 | hg_model = HgModel() | |
20 | c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision) |
|
44 | c.changeset = hg_model.get_repo(c.repo_name).get_changeset(revision) | |
21 | c.changeset_old = c.changeset.parents[0] |
|
45 | c.changeset_old = c.changeset.parents[0] | |
22 | c.changes = [] |
|
46 | c.changes = [] | |
23 |
|
47 | |||
24 |
|
48 | |||
25 | for node in c.changeset.added: |
|
49 | for node in c.changeset.added: | |
26 | filenode_old = FileNode(node.path, '') |
|
50 | filenode_old = FileNode(node.path, '') | |
27 | f_udiff = differ.get_udiff(filenode_old, node) |
|
51 | f_udiff = differ.get_udiff(filenode_old, node) | |
28 | diff = differ.DiffProcessor(f_udiff).as_html() |
|
52 | diff = differ.DiffProcessor(f_udiff).as_html() | |
29 | c.changes.append(('added', node, diff)) |
|
53 | c.changes.append(('added', node, diff)) | |
30 |
|
54 | |||
31 | for node in c.changeset.changed: |
|
55 | for node in c.changeset.changed: | |
32 | filenode_old = c.changeset_old.get_node(node.path) |
|
56 | filenode_old = c.changeset_old.get_node(node.path) | |
33 | f_udiff = differ.get_udiff(filenode_old, node) |
|
57 | f_udiff = differ.get_udiff(filenode_old, node) | |
34 | diff = differ.DiffProcessor(f_udiff).as_html() |
|
58 | diff = differ.DiffProcessor(f_udiff).as_html() | |
35 | c.changes.append(('changed', node, diff)) |
|
59 | c.changes.append(('changed', node, diff)) | |
36 |
|
60 | |||
37 | for node in c.changeset.removed: |
|
61 | for node in c.changeset.removed: | |
38 | c.changes.append(('removed', node, None)) |
|
62 | c.changes.append(('removed', node, None)) | |
39 |
|
63 | |||
40 | return render('changeset/changeset.html') |
|
64 | return render('changeset/changeset.html') |
@@ -1,59 +1,81 b'' | |||||
1 | #!/usr/bin/python |
|
1 | #!/usr/bin/env python | |
2 |
# |
|
2 | # encoding: utf-8 | |
|
3 | # feed controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 23, 2010 | |||
|
22 | feed controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
3 | from pylons import tmpl_context as c, url, response |
|
25 | from pylons import tmpl_context as c, url, response | |
4 | from pylons_app.lib.base import BaseController, render |
|
26 | from pylons_app.lib.base import BaseController, render | |
5 | from pylons_app.model.hg_model import _full_changelog_cached |
|
27 | from pylons_app.model.hg_model import _full_changelog_cached | |
6 | from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed |
|
28 | from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed | |
7 | import logging |
|
29 | import logging | |
8 | log = logging.getLogger(__name__) |
|
30 | log = logging.getLogger(__name__) | |
9 |
|
31 | |||
10 | class FeedController(BaseController): |
|
32 | class FeedController(BaseController): | |
11 |
|
33 | |||
12 | #secure it or not ? |
|
34 | #secure it or not ? | |
13 | def __before__(self): |
|
35 | def __before__(self): | |
14 | super(FeedController, self).__before__() |
|
36 | super(FeedController, self).__before__() | |
15 | #common values for feeds |
|
37 | #common values for feeds | |
16 | self.description = 'Changes on %s repository' |
|
38 | self.description = 'Changes on %s repository' | |
17 | self.title = "%s feed" |
|
39 | self.title = "%s feed" | |
18 | self.language = 'en-us' |
|
40 | self.language = 'en-us' | |
19 | self.ttl = "5" |
|
41 | self.ttl = "5" | |
20 | self.feed_nr = 10 |
|
42 | self.feed_nr = 10 | |
21 |
|
43 | |||
22 | def atom(self, repo_name): |
|
44 | def atom(self, repo_name): | |
23 | """Produce an atom-1.0 feed via feedgenerator module""" |
|
45 | """Produce an atom-1.0 feed via feedgenerator module""" | |
24 | feed = Atom1Feed(title=self.title % repo_name, |
|
46 | feed = Atom1Feed(title=self.title % repo_name, | |
25 | link=url('summary_home', repo_name=repo_name, qualified=True), |
|
47 | link=url('summary_home', repo_name=repo_name, qualified=True), | |
26 | description=self.description % repo_name, |
|
48 | description=self.description % repo_name, | |
27 | language=self.language, |
|
49 | language=self.language, | |
28 | ttl=self.ttl) |
|
50 | ttl=self.ttl) | |
29 |
|
51 | |||
30 |
|
52 | |||
31 | for cnt, cs in enumerate(_full_changelog_cached(repo_name)): |
|
53 | for cnt, cs in enumerate(_full_changelog_cached(repo_name)): | |
32 | if cnt > self.feed_nr: |
|
54 | if cnt > self.feed_nr: | |
33 | break |
|
55 | break | |
34 | feed.add_item(title=cs.message, |
|
56 | feed.add_item(title=cs.message, | |
35 | link=url('changeset_home', repo_name=repo_name, |
|
57 | link=url('changeset_home', repo_name=repo_name, | |
36 | revision=cs.raw_id, qualified=True), |
|
58 | revision=cs.raw_id, qualified=True), | |
37 | description=str(cs.date)) |
|
59 | description=str(cs.date)) | |
38 |
|
60 | |||
39 | response.content_type = feed.mime_type |
|
61 | response.content_type = feed.mime_type | |
40 | return feed.writeString('utf-8') |
|
62 | return feed.writeString('utf-8') | |
41 |
|
63 | |||
42 |
|
64 | |||
43 | def rss(self, repo_name): |
|
65 | def rss(self, repo_name): | |
44 | """Produce an rss2 feed via feedgenerator module""" |
|
66 | """Produce an rss2 feed via feedgenerator module""" | |
45 | feed = Rss201rev2Feed(title=self.title % repo_name, |
|
67 | feed = Rss201rev2Feed(title=self.title % repo_name, | |
46 | link=url('summary_home', repo_name=repo_name, qualified=True), |
|
68 | link=url('summary_home', repo_name=repo_name, qualified=True), | |
47 | description=self.description % repo_name, |
|
69 | description=self.description % repo_name, | |
48 | language=self.language, |
|
70 | language=self.language, | |
49 | ttl=self.ttl) |
|
71 | ttl=self.ttl) | |
50 |
|
72 | |||
51 | for cnt, cs in enumerate(_full_changelog_cached(repo_name)): |
|
73 | for cnt, cs in enumerate(_full_changelog_cached(repo_name)): | |
52 | if cnt > self.feed_nr: |
|
74 | if cnt > self.feed_nr: | |
53 | break |
|
75 | break | |
54 | feed.add_item(title=cs.message, |
|
76 | feed.add_item(title=cs.message, | |
55 | link=url('changeset_home', repo_name=repo_name, revision=cs.raw_id, qualified=True), |
|
77 | link=url('changeset_home', repo_name=repo_name, revision=cs.raw_id, qualified=True), | |
56 | description=str(cs.date)) |
|
78 | description=str(cs.date)) | |
57 |
|
79 | |||
58 | response.content_type = feed.mime_type |
|
80 | response.content_type = feed.mime_type | |
59 | return feed.writeString('utf-8') |
|
81 | return feed.writeString('utf-8') |
@@ -1,154 +1,178 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # files controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 21, 2010 | |||
|
22 | files controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
1 | from mercurial import archival |
|
25 | from mercurial import archival | |
2 | from pylons import request, response, session, tmpl_context as c, url |
|
26 | from pylons import request, response, session, tmpl_context as c, url | |
3 | from pylons_app.lib.auth import LoginRequired |
|
27 | from pylons_app.lib.auth import LoginRequired | |
4 | from pylons_app.lib.base import BaseController, render |
|
28 | from pylons_app.lib.base import BaseController, render | |
5 | from pylons_app.model.hg_model import HgModel |
|
29 | from pylons_app.model.hg_model import HgModel | |
6 | from vcs.exceptions import RepositoryError, ChangesetError |
|
30 | from vcs.exceptions import RepositoryError, ChangesetError | |
7 | from vcs.utils import diffs as differ |
|
31 | from vcs.utils import diffs as differ | |
8 | import logging |
|
32 | import logging | |
9 | import tempfile |
|
33 | import tempfile | |
10 |
|
34 | |||
11 |
|
35 | |||
12 | log = logging.getLogger(__name__) |
|
36 | log = logging.getLogger(__name__) | |
13 |
|
37 | |||
14 | class FilesController(BaseController): |
|
38 | class FilesController(BaseController): | |
15 |
|
39 | |||
16 | @LoginRequired() |
|
40 | @LoginRequired() | |
17 | def __before__(self): |
|
41 | def __before__(self): | |
18 | super(FilesController, self).__before__() |
|
42 | super(FilesController, self).__before__() | |
19 |
|
43 | |||
20 | def index(self, repo_name, revision, f_path): |
|
44 | def index(self, repo_name, revision, f_path): | |
21 | hg_model = HgModel() |
|
45 | hg_model = HgModel() | |
22 | c.repo = repo = hg_model.get_repo(c.repo_name) |
|
46 | c.repo = repo = hg_model.get_repo(c.repo_name) | |
23 | revision = request.POST.get('at_rev', None) or revision |
|
47 | revision = request.POST.get('at_rev', None) or revision | |
24 |
|
48 | |||
25 | def get_next_rev(cur): |
|
49 | def get_next_rev(cur): | |
26 | max_rev = len(c.repo.revisions) - 1 |
|
50 | max_rev = len(c.repo.revisions) - 1 | |
27 | r = cur + 1 |
|
51 | r = cur + 1 | |
28 | if r > max_rev: |
|
52 | if r > max_rev: | |
29 | r = max_rev |
|
53 | r = max_rev | |
30 | return r |
|
54 | return r | |
31 |
|
55 | |||
32 | def get_prev_rev(cur): |
|
56 | def get_prev_rev(cur): | |
33 | r = cur - 1 |
|
57 | r = cur - 1 | |
34 | return r |
|
58 | return r | |
35 |
|
59 | |||
36 | c.f_path = f_path |
|
60 | c.f_path = f_path | |
37 |
|
61 | |||
38 |
|
62 | |||
39 | try: |
|
63 | try: | |
40 | cur_rev = repo.get_changeset(revision).revision |
|
64 | cur_rev = repo.get_changeset(revision).revision | |
41 | prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id |
|
65 | prev_rev = repo.get_changeset(get_prev_rev(cur_rev)).raw_id | |
42 | next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id |
|
66 | next_rev = repo.get_changeset(get_next_rev(cur_rev)).raw_id | |
43 |
|
67 | |||
44 | c.url_prev = url('files_home', repo_name=c.repo_name, |
|
68 | c.url_prev = url('files_home', repo_name=c.repo_name, | |
45 | revision=prev_rev, f_path=f_path) |
|
69 | revision=prev_rev, f_path=f_path) | |
46 | c.url_next = url('files_home', repo_name=c.repo_name, |
|
70 | c.url_next = url('files_home', repo_name=c.repo_name, | |
47 | revision=next_rev, f_path=f_path) |
|
71 | revision=next_rev, f_path=f_path) | |
48 |
|
72 | |||
49 | c.changeset = repo.get_changeset(revision) |
|
73 | c.changeset = repo.get_changeset(revision) | |
50 | try: |
|
74 | try: | |
51 | c.file_msg = c.changeset.get_file_message(f_path) |
|
75 | c.file_msg = c.changeset.get_file_message(f_path) | |
52 | except: |
|
76 | except: | |
53 | c.file_msg = None |
|
77 | c.file_msg = None | |
54 |
|
78 | |||
55 | c.cur_rev = c.changeset.raw_id |
|
79 | c.cur_rev = c.changeset.raw_id | |
56 | c.rev_nr = c.changeset.revision |
|
80 | c.rev_nr = c.changeset.revision | |
57 | c.files_list = c.changeset.get_node(f_path) |
|
81 | c.files_list = c.changeset.get_node(f_path) | |
58 | c.file_history = self._get_history(repo, c.files_list, f_path) |
|
82 | c.file_history = self._get_history(repo, c.files_list, f_path) | |
59 |
|
83 | |||
60 | except (RepositoryError, ChangesetError): |
|
84 | except (RepositoryError, ChangesetError): | |
61 | c.files_list = None |
|
85 | c.files_list = None | |
62 |
|
86 | |||
63 | return render('files/files.html') |
|
87 | return render('files/files.html') | |
64 |
|
88 | |||
65 | def rawfile(self, repo_name, revision, f_path): |
|
89 | def rawfile(self, repo_name, revision, f_path): | |
66 | hg_model = HgModel() |
|
90 | hg_model = HgModel() | |
67 | c.repo = hg_model.get_repo(c.repo_name) |
|
91 | c.repo = hg_model.get_repo(c.repo_name) | |
68 | file_node = c.repo.get_changeset(revision).get_node(f_path) |
|
92 | file_node = c.repo.get_changeset(revision).get_node(f_path) | |
69 | response.content_type = file_node.mimetype |
|
93 | response.content_type = file_node.mimetype | |
70 | response.content_disposition = 'attachment; filename=%s' \ |
|
94 | response.content_disposition = 'attachment; filename=%s' \ | |
71 | % f_path.split('/')[-1] |
|
95 | % f_path.split('/')[-1] | |
72 | return file_node.content |
|
96 | return file_node.content | |
73 |
|
97 | |||
74 | def annotate(self, repo_name, revision, f_path): |
|
98 | def annotate(self, repo_name, revision, f_path): | |
75 | hg_model = HgModel() |
|
99 | hg_model = HgModel() | |
76 | c.repo = hg_model.get_repo(c.repo_name) |
|
100 | c.repo = hg_model.get_repo(c.repo_name) | |
77 | cs = c.repo.get_changeset(revision) |
|
101 | cs = c.repo.get_changeset(revision) | |
78 | c.file = cs.get_node(f_path) |
|
102 | c.file = cs.get_node(f_path) | |
79 | c.file_msg = cs.get_file_message(f_path) |
|
103 | c.file_msg = cs.get_file_message(f_path) | |
80 | c.cur_rev = cs.raw_id |
|
104 | c.cur_rev = cs.raw_id | |
81 | c.f_path = f_path |
|
105 | c.f_path = f_path | |
82 | c.annotate = cs.get_file_annotate(f_path) |
|
106 | c.annotate = cs.get_file_annotate(f_path) | |
83 | return render('files/files_annotate.html') |
|
107 | return render('files/files_annotate.html') | |
84 |
|
108 | |||
85 | def archivefile(self, repo_name, revision, fileformat): |
|
109 | def archivefile(self, repo_name, revision, fileformat): | |
86 | archive_specs = { |
|
110 | archive_specs = { | |
87 | '.tar.bz2': ('application/x-tar', 'tbz2'), |
|
111 | '.tar.bz2': ('application/x-tar', 'tbz2'), | |
88 | '.tar.gz': ('application/x-tar', 'tgz'), |
|
112 | '.tar.gz': ('application/x-tar', 'tgz'), | |
89 | '.zip': ('application/zip', 'zip'), |
|
113 | '.zip': ('application/zip', 'zip'), | |
90 | } |
|
114 | } | |
91 | if not archive_specs.has_key(fileformat): |
|
115 | if not archive_specs.has_key(fileformat): | |
92 | return 'Unknown archive type %s' % fileformat |
|
116 | return 'Unknown archive type %s' % fileformat | |
93 |
|
117 | |||
94 | def read_in_chunks(file_object, chunk_size=1024 * 40): |
|
118 | def read_in_chunks(file_object, chunk_size=1024 * 40): | |
95 | """Lazy function (generator) to read a file piece by piece. |
|
119 | """Lazy function (generator) to read a file piece by piece. | |
96 | Default chunk size: 40k.""" |
|
120 | Default chunk size: 40k.""" | |
97 | while True: |
|
121 | while True: | |
98 | data = file_object.read(chunk_size) |
|
122 | data = file_object.read(chunk_size) | |
99 | if not data: |
|
123 | if not data: | |
100 | break |
|
124 | break | |
101 | yield data |
|
125 | yield data | |
102 |
|
126 | |||
103 | archive = tempfile.TemporaryFile() |
|
127 | archive = tempfile.TemporaryFile() | |
104 | repo = HgModel().get_repo(repo_name).repo |
|
128 | repo = HgModel().get_repo(repo_name).repo | |
105 | fname = '%s-%s%s' % (repo_name, revision, fileformat) |
|
129 | fname = '%s-%s%s' % (repo_name, revision, fileformat) | |
106 | archival.archive(repo, archive, revision, archive_specs[fileformat][1], |
|
130 | archival.archive(repo, archive, revision, archive_specs[fileformat][1], | |
107 | prefix='%s-%s' % (repo_name, revision)) |
|
131 | prefix='%s-%s' % (repo_name, revision)) | |
108 | response.content_type = archive_specs[fileformat][0] |
|
132 | response.content_type = archive_specs[fileformat][0] | |
109 | response.content_disposition = 'attachment; filename=%s' % fname |
|
133 | response.content_disposition = 'attachment; filename=%s' % fname | |
110 | archive.seek(0) |
|
134 | archive.seek(0) | |
111 | return read_in_chunks(archive) |
|
135 | return read_in_chunks(archive) | |
112 |
|
136 | |||
113 | def diff(self, repo_name, f_path): |
|
137 | def diff(self, repo_name, f_path): | |
114 | hg_model = HgModel() |
|
138 | hg_model = HgModel() | |
115 | diff1 = request.GET.get('diff1') |
|
139 | diff1 = request.GET.get('diff1') | |
116 | diff2 = request.GET.get('diff2') |
|
140 | diff2 = request.GET.get('diff2') | |
117 | c.action = action = request.GET.get('diff') |
|
141 | c.action = action = request.GET.get('diff') | |
118 | c.no_changes = diff1 == diff2 |
|
142 | c.no_changes = diff1 == diff2 | |
119 | c.f_path = f_path |
|
143 | c.f_path = f_path | |
120 | c.repo = hg_model.get_repo(c.repo_name) |
|
144 | c.repo = hg_model.get_repo(c.repo_name) | |
121 | c.changeset_1 = c.repo.get_changeset(diff1) |
|
145 | c.changeset_1 = c.repo.get_changeset(diff1) | |
122 | c.changeset_2 = c.repo.get_changeset(diff2) |
|
146 | c.changeset_2 = c.repo.get_changeset(diff2) | |
123 |
|
147 | |||
124 | c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1._short) |
|
148 | c.diff1 = 'r%s:%s' % (c.changeset_1.revision, c.changeset_1._short) | |
125 | c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2._short) |
|
149 | c.diff2 = 'r%s:%s' % (c.changeset_2.revision, c.changeset_2._short) | |
126 | f_udiff = differ.get_udiff(c.changeset_1.get_node(f_path), |
|
150 | f_udiff = differ.get_udiff(c.changeset_1.get_node(f_path), | |
127 | c.changeset_2.get_node(f_path)) |
|
151 | c.changeset_2.get_node(f_path)) | |
128 |
|
152 | |||
129 | diff = differ.DiffProcessor(f_udiff) |
|
153 | diff = differ.DiffProcessor(f_udiff) | |
130 |
|
154 | |||
131 | if action == 'download': |
|
155 | if action == 'download': | |
132 | diff_name = '%s_vs_%s.diff' % (diff1, diff2) |
|
156 | diff_name = '%s_vs_%s.diff' % (diff1, diff2) | |
133 | response.content_type = 'text/plain' |
|
157 | response.content_type = 'text/plain' | |
134 | response.content_disposition = 'attachment; filename=%s' \ |
|
158 | response.content_disposition = 'attachment; filename=%s' \ | |
135 | % diff_name |
|
159 | % diff_name | |
136 | return diff.raw_diff() |
|
160 | return diff.raw_diff() | |
137 |
|
161 | |||
138 | elif action == 'raw': |
|
162 | elif action == 'raw': | |
139 | c.cur_diff = '<pre class="raw">%s</pre>' % diff.raw_diff() |
|
163 | c.cur_diff = '<pre class="raw">%s</pre>' % diff.raw_diff() | |
140 | elif action == 'diff': |
|
164 | elif action == 'diff': | |
141 | c.cur_diff = diff.as_html() |
|
165 | c.cur_diff = diff.as_html() | |
142 |
|
166 | |||
143 | return render('files/file_diff.html') |
|
167 | return render('files/file_diff.html') | |
144 |
|
168 | |||
145 | def _get_history(self, repo, node, f_path): |
|
169 | def _get_history(self, repo, node, f_path): | |
146 | from vcs.nodes import NodeKind |
|
170 | from vcs.nodes import NodeKind | |
147 | if not node.kind is NodeKind.FILE: |
|
171 | if not node.kind is NodeKind.FILE: | |
148 | return [] |
|
172 | return [] | |
149 | changesets = node.history |
|
173 | changesets = node.history | |
150 | hist_l = [] |
|
174 | hist_l = [] | |
151 | for chs in changesets: |
|
175 | for chs in changesets: | |
152 | n_desc = 'r%s:%s' % (chs.revision, chs._short) |
|
176 | n_desc = 'r%s:%s' % (chs.revision, chs._short) | |
153 | hist_l.append((chs._short, n_desc,)) |
|
177 | hist_l.append((chs._short, n_desc,)) | |
154 | return hist_l |
|
178 | return hist_l |
@@ -1,66 +1,84 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # graph controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 21, 2010 | |||
|
22 | graph controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
1 | from mercurial.graphmod import revisions as graph_rev, colored, CHANGESET |
|
25 | from mercurial.graphmod import revisions as graph_rev, colored, CHANGESET | |
2 | from mercurial.node import short |
|
26 | from mercurial.node import short | |
3 |
from pylons import request, |
|
27 | from pylons import request, tmpl_context as c | |
4 | app_globals as g |
|
|||
5 | from pylons.controllers.util import abort, redirect |
|
|||
6 | from pylons_app.lib.auth import LoginRequired |
|
28 | from pylons_app.lib.auth import LoginRequired | |
7 | from pylons_app.lib.base import BaseController, render |
|
29 | from pylons_app.lib.base import BaseController, render | |
8 | from pylons_app.lib.filters import age as _age, person |
|
30 | from pylons_app.lib.filters import age as _age, person | |
9 | from pylons_app.lib.utils import get_repo_slug |
|
|||
10 | from pylons_app.model.hg_model import HgModel |
|
31 | from pylons_app.model.hg_model import HgModel | |
11 | from simplejson import dumps |
|
32 | from simplejson import dumps | |
12 | from webhelpers.paginate import Page |
|
33 | from webhelpers.paginate import Page | |
13 | import logging |
|
34 | import logging | |
14 |
|
35 | |||
15 |
|
||||
16 |
|
||||
17 |
|
||||
18 | log = logging.getLogger(__name__) |
|
36 | log = logging.getLogger(__name__) | |
19 |
|
37 | |||
20 | class GraphController(BaseController): |
|
38 | class GraphController(BaseController): | |
21 |
|
39 | |||
22 | @LoginRequired() |
|
40 | @LoginRequired() | |
23 | def __before__(self): |
|
41 | def __before__(self): | |
24 | super(GraphController, self).__before__() |
|
42 | super(GraphController, self).__before__() | |
25 |
|
43 | |||
26 | def index(self): |
|
44 | def index(self): | |
27 | # Return a rendered template |
|
45 | # Return a rendered template | |
28 | hg_model = HgModel() |
|
46 | hg_model = HgModel() | |
29 | if request.POST.get('size'): |
|
47 | if request.POST.get('size'): | |
30 | c.size = int(request.params.get('size', 20)) |
|
48 | c.size = int(request.params.get('size', 20)) | |
31 | else: |
|
49 | else: | |
32 | c.size = int(request.params.get('size', 20)) |
|
50 | c.size = int(request.params.get('size', 20)) | |
33 | c.jsdata, c.canvasheight = self.graph(hg_model.get_repo(c.repo_name), c.size) |
|
51 | c.jsdata, c.canvasheight = self.graph(hg_model.get_repo(c.repo_name), c.size) | |
34 |
|
52 | |||
35 | return render('/graph.html') |
|
53 | return render('/graph.html') | |
36 |
|
54 | |||
37 |
|
55 | |||
38 | def graph(self, repo, size): |
|
56 | def graph(self, repo, size): | |
39 | revcount = size |
|
57 | revcount = size | |
40 | p = int(request.params.get('page', 1)) |
|
58 | p = int(request.params.get('page', 1)) | |
41 | c.pagination = Page(repo.revisions, page=p, item_count=len(repo.revisions), items_per_page=revcount) |
|
59 | c.pagination = Page(repo.revisions, page=p, item_count=len(repo.revisions), items_per_page=revcount) | |
42 | if not repo.revisions:return dumps([]), 0 |
|
60 | if not repo.revisions:return dumps([]), 0 | |
43 |
|
61 | |||
44 | max_rev = repo.revisions[-1] |
|
62 | max_rev = repo.revisions[-1] | |
45 | offset = 1 if p == 1 else ((p - 1) * revcount) |
|
63 | offset = 1 if p == 1 else ((p - 1) * revcount) | |
46 | rev_start = repo.revisions[(-1 * offset)] |
|
64 | rev_start = repo.revisions[(-1 * offset)] | |
47 | bg_height = 39 |
|
65 | bg_height = 39 | |
48 |
|
66 | |||
49 | revcount = min(max_rev, revcount) |
|
67 | revcount = min(max_rev, revcount) | |
50 | rev_end = max(0, rev_start - revcount) |
|
68 | rev_end = max(0, rev_start - revcount) | |
51 | dag = graph_rev(repo.repo, rev_start, rev_end) |
|
69 | dag = graph_rev(repo.repo, rev_start, rev_end) | |
52 | tree = list(colored(dag)) |
|
70 | tree = list(colored(dag)) | |
53 | canvasheight = (len(tree) + 1) * bg_height - 27 |
|
71 | canvasheight = (len(tree) + 1) * bg_height - 27 | |
54 | data = [] |
|
72 | data = [] | |
55 | for (id, type, ctx, vtx, edges) in tree: |
|
73 | for (id, type, ctx, vtx, edges) in tree: | |
56 | if type != CHANGESET: |
|
74 | if type != CHANGESET: | |
57 | continue |
|
75 | continue | |
58 | node = short(ctx.node()) |
|
76 | node = short(ctx.node()) | |
59 | age = _age(ctx.date()) |
|
77 | age = _age(ctx.date()) | |
60 | desc = ctx.description() |
|
78 | desc = ctx.description() | |
61 | user = person(ctx.user()) |
|
79 | user = person(ctx.user()) | |
62 | branch = ctx.branch() |
|
80 | branch = ctx.branch() | |
63 | branch = branch, repo.repo.branchtags().get(branch) == ctx.node() |
|
81 | branch = branch, repo.repo.branchtags().get(branch) == ctx.node() | |
64 | data.append((node, vtx, edges, desc, user, age, branch, ctx.tags())) |
|
82 | data.append((node, vtx, edges, desc, user, age, branch, ctx.tags())) | |
65 |
|
83 | |||
66 | return dumps(data), canvasheight |
|
84 | return dumps(data), canvasheight |
@@ -1,30 +1,52 b'' | |||||
1 | #!/usr/bin/python |
|
1 | #!/usr/bin/env python | |
2 |
# |
|
2 | # encoding: utf-8 | |
|
3 | # hg controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on February 18, 2010 | |||
|
22 | hg controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
3 | from operator import itemgetter |
|
25 | from operator import itemgetter | |
4 |
from pylons import tmpl_context as c, request |
|
26 | from pylons import tmpl_context as c, request | |
5 | from pylons_app.lib.auth import LoginRequired |
|
27 | from pylons_app.lib.auth import LoginRequired | |
6 | from pylons_app.lib.base import BaseController, render |
|
28 | from pylons_app.lib.base import BaseController, render | |
7 | from pylons_app.model.hg_model import HgModel |
|
29 | from pylons_app.model.hg_model import HgModel | |
8 | import logging |
|
30 | import logging | |
9 | log = logging.getLogger(__name__) |
|
31 | log = logging.getLogger(__name__) | |
10 |
|
32 | |||
11 | class HgController(BaseController): |
|
33 | class HgController(BaseController): | |
12 |
|
34 | |||
13 | @LoginRequired() |
|
35 | @LoginRequired() | |
14 | def __before__(self): |
|
36 | def __before__(self): | |
15 | super(HgController, self).__before__() |
|
37 | super(HgController, self).__before__() | |
16 |
|
38 | |||
17 | def index(self): |
|
39 | def index(self): | |
18 | c.current_sort = request.GET.get('sort', 'name') |
|
40 | c.current_sort = request.GET.get('sort', 'name') | |
19 | cs = c.current_sort |
|
41 | cs = c.current_sort | |
20 | c.cs_slug = cs.replace('-', '') |
|
42 | c.cs_slug = cs.replace('-', '') | |
21 | sortables = ['name', 'description', 'last_change', 'tip', 'contact'] |
|
43 | sortables = ['name', 'description', 'last_change', 'tip', 'contact'] | |
22 | cached_repo_list = HgModel().get_repos() |
|
44 | cached_repo_list = HgModel().get_repos() | |
23 | if cs and c.cs_slug in sortables: |
|
45 | if cs and c.cs_slug in sortables: | |
24 | sort_key = c.cs_slug + '_sort' |
|
46 | sort_key = c.cs_slug + '_sort' | |
25 | if cs.startswith('-'): |
|
47 | if cs.startswith('-'): | |
26 | c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=True) |
|
48 | c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=True) | |
27 | else: |
|
49 | else: | |
28 | c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=False) |
|
50 | c.repos_list = sorted(cached_repo_list, key=itemgetter(sort_key), reverse=False) | |
29 |
|
51 | |||
30 | return render('/index.html') |
|
52 | return render('/index.html') |
@@ -1,42 +1,66 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # login controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 22, 2010 | |||
|
22 | login controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
1 | import logging |
|
25 | import logging | |
2 | from formencode import htmlfill |
|
26 | from formencode import htmlfill | |
3 | from pylons import request, response, session, tmpl_context as c, url |
|
27 | from pylons import request, response, session, tmpl_context as c, url | |
4 | from pylons.controllers.util import abort, redirect |
|
28 | from pylons.controllers.util import abort, redirect | |
5 | from pylons_app.lib.base import BaseController, render |
|
29 | from pylons_app.lib.base import BaseController, render | |
6 | import formencode |
|
30 | import formencode | |
7 | from pylons_app.model.forms import LoginForm |
|
31 | from pylons_app.model.forms import LoginForm | |
8 | from pylons_app.lib.auth import AuthUser |
|
32 | from pylons_app.lib.auth import AuthUser | |
9 |
|
33 | |||
10 | log = logging.getLogger(__name__) |
|
34 | log = logging.getLogger(__name__) | |
11 |
|
35 | |||
12 | class LoginController(BaseController): |
|
36 | class LoginController(BaseController): | |
13 |
|
37 | |||
14 | def __before__(self): |
|
38 | def __before__(self): | |
15 | super(LoginController, self).__before__() |
|
39 | super(LoginController, self).__before__() | |
16 |
|
40 | |||
17 | def index(self): |
|
41 | def index(self): | |
18 | #redirect if already logged in |
|
42 | #redirect if already logged in | |
19 | if c.hg_app_user.is_authenticated: |
|
43 | if c.hg_app_user.is_authenticated: | |
20 | return redirect(url('hg_home')) |
|
44 | return redirect(url('hg_home')) | |
21 |
|
45 | |||
22 | if request.POST: |
|
46 | if request.POST: | |
23 | #import Login Form validator class |
|
47 | #import Login Form validator class | |
24 | login_form = LoginForm() |
|
48 | login_form = LoginForm() | |
25 | try: |
|
49 | try: | |
26 | c.form_result = login_form.to_python(dict(request.POST)) |
|
50 | c.form_result = login_form.to_python(dict(request.POST)) | |
27 | return redirect(url('hg_home')) |
|
51 | return redirect(url('hg_home')) | |
28 |
|
52 | |||
29 | except formencode.Invalid as errors: |
|
53 | except formencode.Invalid as errors: | |
30 | c.form_errors = errors.error_dict |
|
54 | c.form_errors = errors.error_dict | |
31 | return htmlfill.render( |
|
55 | return htmlfill.render( | |
32 | render('/login.html'), |
|
56 | render('/login.html'), | |
33 | defaults=errors.value, |
|
57 | defaults=errors.value, | |
34 | encoding="UTF-8") |
|
58 | encoding="UTF-8") | |
35 |
|
59 | |||
36 | return render('/login.html') |
|
60 | return render('/login.html') | |
37 |
|
61 | |||
38 | def logout(self): |
|
62 | def logout(self): | |
39 | session['hg_app_user'] = AuthUser() |
|
63 | session['hg_app_user'] = AuthUser() | |
40 | session.save() |
|
64 | session.save() | |
41 | log.info('Logging out and setting user as Empty') |
|
65 | log.info('Logging out and setting user as Empty') | |
42 | redirect(url('hg_home')) |
|
66 | redirect(url('hg_home')) |
@@ -1,53 +1,77 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # permissions controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 27, 2010 | |||
|
22 | permissions controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
1 | import logging |
|
25 | import logging | |
2 |
|
26 | |||
3 | from pylons import request, response, session, tmpl_context as c, url |
|
27 | from pylons import request, response, session, tmpl_context as c, url | |
4 | from pylons.controllers.util import abort, redirect |
|
28 | from pylons.controllers.util import abort, redirect | |
5 |
|
29 | |||
6 | from pylons_app.lib.base import BaseController, render |
|
30 | from pylons_app.lib.base import BaseController, render | |
7 |
|
31 | |||
8 | log = logging.getLogger(__name__) |
|
32 | log = logging.getLogger(__name__) | |
9 |
|
33 | |||
10 | class PermissionsController(BaseController): |
|
34 | class PermissionsController(BaseController): | |
11 | """REST Controller styled on the Atom Publishing Protocol""" |
|
35 | """REST Controller styled on the Atom Publishing Protocol""" | |
12 | # To properly map this controller, ensure your config/routing.py |
|
36 | # To properly map this controller, ensure your config/routing.py | |
13 | # file has a resource setup: |
|
37 | # file has a resource setup: | |
14 | # map.resource('permission', 'permissions') |
|
38 | # map.resource('permission', 'permissions') | |
15 |
|
39 | |||
16 | def index(self, format='html'): |
|
40 | def index(self, format='html'): | |
17 | """GET /permissions: All items in the collection""" |
|
41 | """GET /permissions: All items in the collection""" | |
18 | # url('permissions') |
|
42 | # url('permissions') | |
19 | return render('admin/permissions/permissions.html') |
|
43 | return render('admin/permissions/permissions.html') | |
20 |
|
44 | |||
21 | def create(self): |
|
45 | def create(self): | |
22 | """POST /permissions: Create a new item""" |
|
46 | """POST /permissions: Create a new item""" | |
23 | # url('permissions') |
|
47 | # url('permissions') | |
24 |
|
48 | |||
25 | def new(self, format='html'): |
|
49 | def new(self, format='html'): | |
26 | """GET /permissions/new: Form to create a new item""" |
|
50 | """GET /permissions/new: Form to create a new item""" | |
27 | # url('new_permission') |
|
51 | # url('new_permission') | |
28 |
|
52 | |||
29 | def update(self, id): |
|
53 | def update(self, id): | |
30 | """PUT /permissions/id: Update an existing item""" |
|
54 | """PUT /permissions/id: Update an existing item""" | |
31 | # Forms posted to this method should contain a hidden field: |
|
55 | # Forms posted to this method should contain a hidden field: | |
32 | # <input type="hidden" name="_method" value="PUT" /> |
|
56 | # <input type="hidden" name="_method" value="PUT" /> | |
33 | # Or using helpers: |
|
57 | # Or using helpers: | |
34 | # h.form(url('permission', id=ID), |
|
58 | # h.form(url('permission', id=ID), | |
35 | # method='put') |
|
59 | # method='put') | |
36 | # url('permission', id=ID) |
|
60 | # url('permission', id=ID) | |
37 |
|
61 | |||
38 | def delete(self, id): |
|
62 | def delete(self, id): | |
39 | """DELETE /permissions/id: Delete an existing item""" |
|
63 | """DELETE /permissions/id: Delete an existing item""" | |
40 | # Forms posted to this method should contain a hidden field: |
|
64 | # Forms posted to this method should contain a hidden field: | |
41 | # <input type="hidden" name="_method" value="DELETE" /> |
|
65 | # <input type="hidden" name="_method" value="DELETE" /> | |
42 | # Or using helpers: |
|
66 | # Or using helpers: | |
43 | # h.form(url('permission', id=ID), |
|
67 | # h.form(url('permission', id=ID), | |
44 | # method='delete') |
|
68 | # method='delete') | |
45 | # url('permission', id=ID) |
|
69 | # url('permission', id=ID) | |
46 |
|
70 | |||
47 | def show(self, id, format='html'): |
|
71 | def show(self, id, format='html'): | |
48 | """GET /permissions/id: Show a specific item""" |
|
72 | """GET /permissions/id: Show a specific item""" | |
49 | # url('permission', id=ID) |
|
73 | # url('permission', id=ID) | |
50 |
|
74 | |||
51 | def edit(self, id, format='html'): |
|
75 | def edit(self, id, format='html'): | |
52 | """GET /permissions/id/edit: Form to edit an existing item""" |
|
76 | """GET /permissions/id/edit: Form to edit an existing item""" | |
53 | # url('edit_permission', id=ID) |
|
77 | # url('edit_permission', id=ID) |
@@ -1,103 +1,127 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # repos controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 7, 2010 | |||
|
22 | admin controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
|
25 | import logging | |||
1 | from pylons import request, response, session, tmpl_context as c, url, \ |
|
26 | from pylons import request, response, session, tmpl_context as c, url, \ | |
2 | app_globals as g |
|
27 | app_globals as g | |
3 | from pylons.controllers.util import abort, redirect |
|
28 | from pylons.controllers.util import abort, redirect | |
4 | from pylons_app.lib.auth import LoginRequired |
|
29 | from pylons_app.lib.auth import LoginRequired | |
5 | from pylons.i18n.translation import _ |
|
30 | from pylons.i18n.translation import _ | |
6 | from pylons_app.lib import helpers as h |
|
31 | from pylons_app.lib import helpers as h | |
7 | from pylons_app.lib.base import BaseController, render |
|
32 | from pylons_app.lib.base import BaseController, render | |
8 | from pylons_app.lib.filters import clean_repo |
|
33 | from pylons_app.lib.filters import clean_repo | |
9 | from pylons_app.lib.utils import check_repo, invalidate_cache |
|
34 | from pylons_app.lib.utils import check_repo, invalidate_cache | |
10 | from pylons_app.model.hg_model import HgModel |
|
35 | from pylons_app.model.hg_model import HgModel | |
11 | import logging |
|
|||
12 | import os |
|
36 | import os | |
13 | import shutil |
|
37 | import shutil | |
14 | from operator import itemgetter |
|
38 | from operator import itemgetter | |
15 | log = logging.getLogger(__name__) |
|
39 | log = logging.getLogger(__name__) | |
16 |
|
40 | |||
17 | class ReposController(BaseController): |
|
41 | class ReposController(BaseController): | |
18 | """REST Controller styled on the Atom Publishing Protocol""" |
|
42 | """REST Controller styled on the Atom Publishing Protocol""" | |
19 | # To properly map this controller, ensure your config/routing.py |
|
43 | # To properly map this controller, ensure your config/routing.py | |
20 | # file has a resource setup: |
|
44 | # file has a resource setup: | |
21 | # map.resource('repo', 'repos') |
|
45 | # map.resource('repo', 'repos') | |
22 | @LoginRequired() |
|
46 | @LoginRequired() | |
23 | def __before__(self): |
|
47 | def __before__(self): | |
24 | c.admin_user = session.get('admin_user') |
|
48 | c.admin_user = session.get('admin_user') | |
25 | c.admin_username = session.get('admin_username') |
|
49 | c.admin_username = session.get('admin_username') | |
26 | super(ReposController, self).__before__() |
|
50 | super(ReposController, self).__before__() | |
27 |
|
51 | |||
28 | def index(self, format='html'): |
|
52 | def index(self, format='html'): | |
29 | """GET /repos: All items in the collection""" |
|
53 | """GET /repos: All items in the collection""" | |
30 | # url('repos') |
|
54 | # url('repos') | |
31 | cached_repo_list = HgModel().get_repos() |
|
55 | cached_repo_list = HgModel().get_repos() | |
32 | c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort')) |
|
56 | c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort')) | |
33 | return render('admin/repos/repos.html') |
|
57 | return render('admin/repos/repos.html') | |
34 |
|
58 | |||
35 | def create(self): |
|
59 | def create(self): | |
36 | """POST /repos: Create a new item""" |
|
60 | """POST /repos: Create a new item""" | |
37 | # url('repos') |
|
61 | # url('repos') | |
38 | name = request.POST.get('name') |
|
62 | name = request.POST.get('name') | |
39 |
|
63 | |||
40 | try: |
|
64 | try: | |
41 | self._create_repo(name) |
|
65 | self._create_repo(name) | |
42 | #clear our cached list for refresh with new repo |
|
66 | #clear our cached list for refresh with new repo | |
43 | invalidate_cache('cached_repo_list') |
|
67 | invalidate_cache('cached_repo_list') | |
44 | h.flash(_('created repository %s') % name, category='success') |
|
68 | h.flash(_('created repository %s') % name, category='success') | |
45 | except Exception as e: |
|
69 | except Exception as e: | |
46 | log.error(e) |
|
70 | log.error(e) | |
47 |
|
71 | |||
48 | return redirect('repos') |
|
72 | return redirect('repos') | |
49 |
|
73 | |||
50 | def _create_repo(self, repo_name): |
|
74 | def _create_repo(self, repo_name): | |
51 | repo_path = os.path.join(g.base_path, repo_name) |
|
75 | repo_path = os.path.join(g.base_path, repo_name) | |
52 | if check_repo(repo_name, g.base_path): |
|
76 | if check_repo(repo_name, g.base_path): | |
53 | log.info('creating repo %s in %s', repo_name, repo_path) |
|
77 | log.info('creating repo %s in %s', repo_name, repo_path) | |
54 | from vcs.backends.hg import MercurialRepository |
|
78 | from vcs.backends.hg import MercurialRepository | |
55 | MercurialRepository(repo_path, create=True) |
|
79 | MercurialRepository(repo_path, create=True) | |
56 |
|
80 | |||
57 |
|
81 | |||
58 | def new(self, format='html'): |
|
82 | def new(self, format='html'): | |
59 | """GET /repos/new: Form to create a new item""" |
|
83 | """GET /repos/new: Form to create a new item""" | |
60 | new_repo = request.GET.get('repo', '') |
|
84 | new_repo = request.GET.get('repo', '') | |
61 | c.new_repo = clean_repo(new_repo) |
|
85 | c.new_repo = clean_repo(new_repo) | |
62 |
|
86 | |||
63 | return render('admin/repos/repo_add.html') |
|
87 | return render('admin/repos/repo_add.html') | |
64 |
|
88 | |||
65 | def update(self, id): |
|
89 | def update(self, id): | |
66 | """PUT /repos/id: Update an existing item""" |
|
90 | """PUT /repos/id: Update an existing item""" | |
67 | # Forms posted to this method should contain a hidden field: |
|
91 | # Forms posted to this method should contain a hidden field: | |
68 | # <input type="hidden" name="_method" value="PUT" /> |
|
92 | # <input type="hidden" name="_method" value="PUT" /> | |
69 | # Or using helpers: |
|
93 | # Or using helpers: | |
70 | # h.form(url('repo', id=ID), |
|
94 | # h.form(url('repo', id=ID), | |
71 | # method='put') |
|
95 | # method='put') | |
72 | # url('repo', id=ID) |
|
96 | # url('repo', id=ID) | |
73 |
|
97 | |||
74 | def delete(self, id): |
|
98 | def delete(self, id): | |
75 | """DELETE /repos/id: Delete an existing item""" |
|
99 | """DELETE /repos/id: Delete an existing item""" | |
76 | # Forms posted to this method should contain a hidden field: |
|
100 | # Forms posted to this method should contain a hidden field: | |
77 | # <input type="hidden" name="_method" value="DELETE" /> |
|
101 | # <input type="hidden" name="_method" value="DELETE" /> | |
78 | # Or using helpers: |
|
102 | # Or using helpers: | |
79 | # h.form(url('repo', id=ID), |
|
103 | # h.form(url('repo', id=ID), | |
80 | # method='delete') |
|
104 | # method='delete') | |
81 | # url('repo', id=ID) |
|
105 | # url('repo', id=ID) | |
82 | from datetime import datetime |
|
106 | from datetime import datetime | |
83 | path = g.paths[0][1].replace('*', '') |
|
107 | path = g.paths[0][1].replace('*', '') | |
84 | rm_path = os.path.join(path, id) |
|
108 | rm_path = os.path.join(path, id) | |
85 | log.info("Removing %s", rm_path) |
|
109 | log.info("Removing %s", rm_path) | |
86 | shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg')) |
|
110 | shutil.move(os.path.join(rm_path, '.hg'), os.path.join(rm_path, 'rm__.hg')) | |
87 | shutil.move(rm_path, os.path.join(path, 'rm__%s-%s' % (datetime.today(), id))) |
|
111 | shutil.move(rm_path, os.path.join(path, 'rm__%s-%s' % (datetime.today(), id))) | |
88 |
|
112 | |||
89 | #clear our cached list for refresh with new repo |
|
113 | #clear our cached list for refresh with new repo | |
90 | invalidate_cache('cached_repo_list') |
|
114 | invalidate_cache('cached_repo_list') | |
91 | h.flash(_('deleted repository %s') % rm_path, category='success') |
|
115 | h.flash(_('deleted repository %s') % rm_path, category='success') | |
92 | return redirect(url('repos')) |
|
116 | return redirect(url('repos')) | |
93 |
|
117 | |||
94 |
|
118 | |||
95 | def show(self, id, format='html'): |
|
119 | def show(self, id, format='html'): | |
96 | """GET /repos/id: Show a specific item""" |
|
120 | """GET /repos/id: Show a specific item""" | |
97 | # url('repo', id=ID) |
|
121 | # url('repo', id=ID) | |
98 |
|
122 | |||
99 | def edit(self, id, format='html'): |
|
123 | def edit(self, id, format='html'): | |
100 | """GET /repos/id/edit: Form to edit an existing item""" |
|
124 | """GET /repos/id/edit: Form to edit an existing item""" | |
101 | # url('edit_repo', id=ID) |
|
125 | # url('edit_repo', id=ID) | |
102 | c.new_repo = id |
|
126 | c.new_repo = id | |
103 | return render('admin/repos/repo_edit.html') |
|
127 | return render('admin/repos/repo_edit.html') |
@@ -1,25 +1,49 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # shortlog controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 18, 2010 | |||
|
22 | shortlog controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
1 | from pylons import tmpl_context as c, request |
|
25 | from pylons import tmpl_context as c, request | |
2 | from pylons_app.lib.auth import LoginRequired |
|
26 | from pylons_app.lib.auth import LoginRequired | |
3 | from pylons_app.lib.base import BaseController, render |
|
27 | from pylons_app.lib.base import BaseController, render | |
4 | from pylons_app.model.hg_model import HgModel |
|
28 | from pylons_app.model.hg_model import HgModel | |
5 | from webhelpers.paginate import Page |
|
29 | from webhelpers.paginate import Page | |
6 | import logging |
|
30 | import logging | |
7 |
|
31 | |||
8 | log = logging.getLogger(__name__) |
|
32 | log = logging.getLogger(__name__) | |
9 |
|
33 | |||
10 | class ShortlogController(BaseController): |
|
34 | class ShortlogController(BaseController): | |
11 |
|
35 | |||
12 | @LoginRequired() |
|
36 | @LoginRequired() | |
13 | def __before__(self): |
|
37 | def __before__(self): | |
14 | super(ShortlogController, self).__before__() |
|
38 | super(ShortlogController, self).__before__() | |
15 |
|
39 | |||
16 | def index(self): |
|
40 | def index(self): | |
17 | hg_model = HgModel() |
|
41 | hg_model = HgModel() | |
18 | p = int(request.params.get('page', 1)) |
|
42 | p = int(request.params.get('page', 1)) | |
19 | repo = hg_model.get_repo(c.repo_name) |
|
43 | repo = hg_model.get_repo(c.repo_name) | |
20 | c.repo_changesets = Page(repo, page=p, items_per_page=20) |
|
44 | c.repo_changesets = Page(repo, page=p, items_per_page=20) | |
21 | c.shortlog_data = render('shortlog/shortlog_data.html') |
|
45 | c.shortlog_data = render('shortlog/shortlog_data.html') | |
22 | if request.params.get('partial'): |
|
46 | if request.params.get('partial'): | |
23 | return c.shortlog_data |
|
47 | return c.shortlog_data | |
24 | r = render('shortlog/shortlog.html') |
|
48 | r = render('shortlog/shortlog.html') | |
25 | return r |
|
49 | return r |
@@ -1,28 +1,52 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # summary controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 18, 2010 | |||
|
22 | summary controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
1 | from pylons import tmpl_context as c, request |
|
25 | from pylons import tmpl_context as c, request | |
2 | from pylons_app.lib.auth import LoginRequired |
|
26 | from pylons_app.lib.auth import LoginRequired | |
3 | from pylons_app.lib.base import BaseController, render |
|
27 | from pylons_app.lib.base import BaseController, render | |
4 | from pylons_app.model.hg_model import HgModel, _full_changelog_cached |
|
28 | from pylons_app.model.hg_model import HgModel, _full_changelog_cached | |
5 | import logging |
|
29 | import logging | |
6 |
|
30 | |||
7 | log = logging.getLogger(__name__) |
|
31 | log = logging.getLogger(__name__) | |
8 |
|
32 | |||
9 | class SummaryController(BaseController): |
|
33 | class SummaryController(BaseController): | |
10 |
|
34 | |||
11 | @LoginRequired() |
|
35 | @LoginRequired() | |
12 | def __before__(self): |
|
36 | def __before__(self): | |
13 | super(SummaryController, self).__before__() |
|
37 | super(SummaryController, self).__before__() | |
14 |
|
38 | |||
15 | def index(self): |
|
39 | def index(self): | |
16 | hg_model = HgModel() |
|
40 | hg_model = HgModel() | |
17 | c.repo_info = hg_model.get_repo(c.repo_name) |
|
41 | c.repo_info = hg_model.get_repo(c.repo_name) | |
18 | c.repo_changesets = _full_changelog_cached(c.repo_name)[:10] |
|
42 | c.repo_changesets = _full_changelog_cached(c.repo_name)[:10] | |
19 | e = request.environ |
|
43 | e = request.environ | |
20 | uri = u'%(protocol)s://%(user)s@%(host)s/%(repo_name)s' % { |
|
44 | uri = u'%(protocol)s://%(user)s@%(host)s/%(repo_name)s' % { | |
21 | 'protocol': e.get('wsgi.url_scheme'), |
|
45 | 'protocol': e.get('wsgi.url_scheme'), | |
22 | 'user':str(c.hg_app_user.username), |
|
46 | 'user':str(c.hg_app_user.username), | |
23 | 'host':e.get('HTTP_HOST'), |
|
47 | 'host':e.get('HTTP_HOST'), | |
24 | 'repo_name':c.repo_name, } |
|
48 | 'repo_name':c.repo_name, } | |
25 | c.clone_repo_url = uri |
|
49 | c.clone_repo_url = uri | |
26 | c.repo_tags = c.repo_info.tags[:10] |
|
50 | c.repo_tags = c.repo_info.tags[:10] | |
27 | c.repo_branches = c.repo_info.branches[:10] |
|
51 | c.repo_branches = c.repo_info.branches[:10] | |
28 | return render('summary/summary.html') |
|
52 | return render('summary/summary.html') |
@@ -1,20 +1,44 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # tags controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 21, 2010 | |||
|
22 | tags controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
1 | from pylons import tmpl_context as c |
|
25 | from pylons import tmpl_context as c | |
2 | from pylons_app.lib.auth import LoginRequired |
|
26 | from pylons_app.lib.auth import LoginRequired | |
3 | from pylons_app.lib.base import BaseController, render |
|
27 | from pylons_app.lib.base import BaseController, render | |
4 | from pylons_app.model.hg_model import HgModel |
|
28 | from pylons_app.model.hg_model import HgModel | |
5 | import logging |
|
29 | import logging | |
6 |
|
30 | |||
7 | log = logging.getLogger(__name__) |
|
31 | log = logging.getLogger(__name__) | |
8 |
|
32 | |||
9 | class TagsController(BaseController): |
|
33 | class TagsController(BaseController): | |
10 |
|
34 | |||
11 | @LoginRequired() |
|
35 | @LoginRequired() | |
12 | def __before__(self): |
|
36 | def __before__(self): | |
13 | super(TagsController, self).__before__() |
|
37 | super(TagsController, self).__before__() | |
14 |
|
38 | |||
15 | def index(self): |
|
39 | def index(self): | |
16 | hg_model = HgModel() |
|
40 | hg_model = HgModel() | |
17 | c.repo_info = hg_model.get_repo(c.repo_name) |
|
41 | c.repo_info = hg_model.get_repo(c.repo_name) | |
18 | c.repo_tags = c.repo_info.tags |
|
42 | c.repo_tags = c.repo_info.tags | |
19 |
|
43 | |||
20 | return render('tags/tags.html') |
|
44 | return render('tags/tags.html') |
@@ -1,115 +1,139 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # users controller for pylons | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 4, 2010 | |||
|
22 | users controller for pylons | |||
|
23 | @author: marcink | |||
|
24 | """ | |||
|
25 | import logging | |||
1 | from formencode import htmlfill |
|
26 | from formencode import htmlfill | |
2 | from pylons import request, session, tmpl_context as c, url |
|
27 | from pylons import request, session, tmpl_context as c, url | |
3 | from pylons.controllers.util import abort, redirect |
|
28 | from pylons.controllers.util import abort, redirect | |
4 | from pylons.i18n.translation import _ |
|
29 | from pylons.i18n.translation import _ | |
5 | from pylons_app.lib import helpers as h |
|
30 | from pylons_app.lib import helpers as h | |
6 | from pylons_app.lib.auth import LoginRequired, CheckPermissionAll |
|
31 | from pylons_app.lib.auth import LoginRequired, CheckPermissionAll | |
7 | from pylons_app.lib.base import BaseController, render |
|
32 | from pylons_app.lib.base import BaseController, render | |
8 | from pylons_app.model.db import User, UserLog |
|
33 | from pylons_app.model.db import User, UserLog | |
9 | from pylons_app.model.forms import UserForm |
|
34 | from pylons_app.model.forms import UserForm | |
10 | from pylons_app.model.user_model import UserModel |
|
35 | from pylons_app.model.user_model import UserModel | |
11 | import formencode |
|
36 | import formencode | |
12 | import logging |
|
|||
13 |
|
37 | |||
14 | log = logging.getLogger(__name__) |
|
38 | log = logging.getLogger(__name__) | |
15 |
|
39 | |||
16 | class UsersController(BaseController): |
|
40 | class UsersController(BaseController): | |
17 | """REST Controller styled on the Atom Publishing Protocol""" |
|
41 | """REST Controller styled on the Atom Publishing Protocol""" | |
18 | # To properly map this controller, ensure your config/routing.py |
|
42 | # To properly map this controller, ensure your config/routing.py | |
19 | # file has a resource setup: |
|
43 | # file has a resource setup: | |
20 | # map.resource('user', 'users') |
|
44 | # map.resource('user', 'users') | |
21 | @LoginRequired() |
|
45 | @LoginRequired() | |
22 | def __before__(self): |
|
46 | def __before__(self): | |
23 | c.admin_user = session.get('admin_user') |
|
47 | c.admin_user = session.get('admin_user') | |
24 | c.admin_username = session.get('admin_username') |
|
48 | c.admin_username = session.get('admin_username') | |
25 | super(UsersController, self).__before__() |
|
49 | super(UsersController, self).__before__() | |
26 |
|
50 | |||
27 |
|
51 | |||
28 | def index(self, format='html'): |
|
52 | def index(self, format='html'): | |
29 | """GET /users: All items in the collection""" |
|
53 | """GET /users: All items in the collection""" | |
30 | # url('users') |
|
54 | # url('users') | |
31 |
|
55 | |||
32 | c.users_list = self.sa.query(User).all() |
|
56 | c.users_list = self.sa.query(User).all() | |
33 | return render('admin/users/users.html') |
|
57 | return render('admin/users/users.html') | |
34 |
|
58 | |||
35 | def create(self): |
|
59 | def create(self): | |
36 | """POST /users: Create a new item""" |
|
60 | """POST /users: Create a new item""" | |
37 | # url('users') |
|
61 | # url('users') | |
38 |
|
62 | |||
39 | user_model = UserModel() |
|
63 | user_model = UserModel() | |
40 | login_form = UserForm()() |
|
64 | login_form = UserForm()() | |
41 | try: |
|
65 | try: | |
42 | form_result = login_form.to_python(dict(request.POST)) |
|
66 | form_result = login_form.to_python(dict(request.POST)) | |
43 | user_model.create(form_result) |
|
67 | user_model.create(form_result) | |
44 | h.flash(_('created user %s') % form_result['username'], category='success') |
|
68 | h.flash(_('created user %s') % form_result['username'], category='success') | |
45 | return redirect(url('users')) |
|
69 | return redirect(url('users')) | |
46 |
|
70 | |||
47 | except formencode.Invalid as errors: |
|
71 | except formencode.Invalid as errors: | |
48 | c.form_errors = errors.error_dict |
|
72 | c.form_errors = errors.error_dict | |
49 | return htmlfill.render( |
|
73 | return htmlfill.render( | |
50 | render('admin/users/user_add.html'), |
|
74 | render('admin/users/user_add.html'), | |
51 | defaults=errors.value, |
|
75 | defaults=errors.value, | |
52 | encoding="UTF-8") |
|
76 | encoding="UTF-8") | |
53 |
|
77 | |||
54 | def new(self, format='html'): |
|
78 | def new(self, format='html'): | |
55 | """GET /users/new: Form to create a new item""" |
|
79 | """GET /users/new: Form to create a new item""" | |
56 | # url('new_user') |
|
80 | # url('new_user') | |
57 | return render('admin/users/user_add.html') |
|
81 | return render('admin/users/user_add.html') | |
58 |
|
82 | |||
59 | def update(self, id): |
|
83 | def update(self, id): | |
60 | """PUT /users/id: Update an existing item""" |
|
84 | """PUT /users/id: Update an existing item""" | |
61 | # Forms posted to this method should contain a hidden field: |
|
85 | # Forms posted to this method should contain a hidden field: | |
62 | # <input type="hidden" name="_method" value="PUT" /> |
|
86 | # <input type="hidden" name="_method" value="PUT" /> | |
63 | # Or using helpers: |
|
87 | # Or using helpers: | |
64 | # h.form(url('user', id=ID), |
|
88 | # h.form(url('user', id=ID), | |
65 | # method='put') |
|
89 | # method='put') | |
66 | # url('user', id=ID) |
|
90 | # url('user', id=ID) | |
67 | user_model = UserModel() |
|
91 | user_model = UserModel() | |
68 | login_form = UserForm(edit=True)() |
|
92 | login_form = UserForm(edit=True)() | |
69 | try: |
|
93 | try: | |
70 | form_result = login_form.to_python(dict(request.POST)) |
|
94 | form_result = login_form.to_python(dict(request.POST)) | |
71 | user_model.update(id, form_result) |
|
95 | user_model.update(id, form_result) | |
72 | h.flash(_('User updated succesfully'), category='success') |
|
96 | h.flash(_('User updated succesfully'), category='success') | |
73 | return redirect(url('users')) |
|
97 | return redirect(url('users')) | |
74 |
|
98 | |||
75 | except formencode.Invalid as errors: |
|
99 | except formencode.Invalid as errors: | |
76 | c.user = user_model.get_user(id) |
|
100 | c.user = user_model.get_user(id) | |
77 | c.form_errors = errors.error_dict |
|
101 | c.form_errors = errors.error_dict | |
78 | return htmlfill.render( |
|
102 | return htmlfill.render( | |
79 | render('admin/users/user_edit.html'), |
|
103 | render('admin/users/user_edit.html'), | |
80 | defaults=errors.value, |
|
104 | defaults=errors.value, | |
81 | encoding="UTF-8") |
|
105 | encoding="UTF-8") | |
82 |
|
106 | |||
83 | def delete(self, id): |
|
107 | def delete(self, id): | |
84 | """DELETE /users/id: Delete an existing item""" |
|
108 | """DELETE /users/id: Delete an existing item""" | |
85 | # Forms posted to this method should contain a hidden field: |
|
109 | # Forms posted to this method should contain a hidden field: | |
86 | # <input type="hidden" name="_method" value="DELETE" /> |
|
110 | # <input type="hidden" name="_method" value="DELETE" /> | |
87 | # Or using helpers: |
|
111 | # Or using helpers: | |
88 | # h.form(url('user', id=ID), |
|
112 | # h.form(url('user', id=ID), | |
89 | # method='delete') |
|
113 | # method='delete') | |
90 | # url('user', id=ID) |
|
114 | # url('user', id=ID) | |
91 | try: |
|
115 | try: | |
92 | self.sa.delete(self.sa.query(User).get(id)) |
|
116 | self.sa.delete(self.sa.query(User).get(id)) | |
93 | self.sa.commit() |
|
117 | self.sa.commit() | |
94 | h.flash(_('sucessfully deleted user'), category='success') |
|
118 | h.flash(_('sucessfully deleted user'), category='success') | |
95 | except: |
|
119 | except: | |
96 | self.sa.rollback() |
|
120 | self.sa.rollback() | |
97 | raise |
|
121 | raise | |
98 | return redirect(url('users')) |
|
122 | return redirect(url('users')) | |
99 |
|
123 | |||
100 | def show(self, id, format='html'): |
|
124 | def show(self, id, format='html'): | |
101 | """GET /users/id: Show a specific item""" |
|
125 | """GET /users/id: Show a specific item""" | |
102 | # url('user', id=ID) |
|
126 | # url('user', id=ID) | |
103 |
|
127 | |||
104 |
|
128 | |||
105 | def edit(self, id, format='html'): |
|
129 | def edit(self, id, format='html'): | |
106 | """GET /users/id/edit: Form to edit an existing item""" |
|
130 | """GET /users/id/edit: Form to edit an existing item""" | |
107 | # url('edit_user', id=ID) |
|
131 | # url('edit_user', id=ID) | |
108 | c.user = self.sa.query(User).get(id) |
|
132 | c.user = self.sa.query(User).get(id) | |
109 | defaults = c.user.__dict__ |
|
133 | defaults = c.user.__dict__ | |
110 | return htmlfill.render( |
|
134 | return htmlfill.render( | |
111 | render('admin/users/user_edit.html'), |
|
135 | render('admin/users/user_edit.html'), | |
112 | defaults=defaults, |
|
136 | defaults=defaults, | |
113 | encoding="UTF-8", |
|
137 | encoding="UTF-8", | |
114 | force_defaults=False |
|
138 | force_defaults=False | |
115 | ) |
|
139 | ) |
@@ -1,153 +1,178 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # authentication and permission libraries | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | """ | |||
|
21 | Created on April 4, 2010 | |||
|
22 | ||||
|
23 | @author: marcink | |||
|
24 | """ | |||
|
25 | ||||
1 | from functools import wraps |
|
26 | from functools import wraps | |
2 | from pylons import session, url, app_globals as g |
|
27 | from pylons import session, url, app_globals as g | |
3 | from pylons.controllers.util import abort, redirect |
|
28 | from pylons.controllers.util import abort, redirect | |
4 | from pylons_app.model import meta |
|
29 | from pylons_app.model import meta | |
5 | from pylons_app.model.db import User |
|
30 | from pylons_app.model.db import User | |
6 | from sqlalchemy.exc import OperationalError |
|
31 | from sqlalchemy.exc import OperationalError | |
7 | from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound |
|
32 | from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound | |
8 | import crypt |
|
33 | import crypt | |
9 | import logging |
|
34 | import logging | |
10 | log = logging.getLogger(__name__) |
|
35 | log = logging.getLogger(__name__) | |
11 |
|
36 | |||
12 | def get_crypt_password(password): |
|
37 | def get_crypt_password(password): | |
13 | """ |
|
38 | """ | |
14 | Cryptographic function used for password hashing |
|
39 | Cryptographic function used for password hashing | |
15 | @param password: password to hash |
|
40 | @param password: password to hash | |
16 | """ |
|
41 | """ | |
17 | return crypt.crypt(password, '6a') |
|
42 | return crypt.crypt(password, '6a') | |
18 |
|
43 | |||
19 | def authfunc(environ, username, password): |
|
44 | def authfunc(environ, username, password): | |
20 | sa = meta.Session |
|
45 | sa = meta.Session | |
21 | password_crypt = get_crypt_password(password) |
|
46 | password_crypt = get_crypt_password(password) | |
22 | try: |
|
47 | try: | |
23 | user = sa.query(User).filter(User.username == username).one() |
|
48 | user = sa.query(User).filter(User.username == username).one() | |
24 | except (NoResultFound, MultipleResultsFound, OperationalError) as e: |
|
49 | except (NoResultFound, MultipleResultsFound, OperationalError) as e: | |
25 | log.error(e) |
|
50 | log.error(e) | |
26 | user = None |
|
51 | user = None | |
27 |
|
52 | |||
28 | if user: |
|
53 | if user: | |
29 | if user.active: |
|
54 | if user.active: | |
30 | if user.username == username and user.password == password_crypt: |
|
55 | if user.username == username and user.password == password_crypt: | |
31 | log.info('user %s authenticated correctly', username) |
|
56 | log.info('user %s authenticated correctly', username) | |
32 | return True |
|
57 | return True | |
33 | else: |
|
58 | else: | |
34 | log.error('user %s is disabled', username) |
|
59 | log.error('user %s is disabled', username) | |
35 |
|
60 | |||
36 | return False |
|
61 | return False | |
37 |
|
62 | |||
38 | class AuthUser(object): |
|
63 | class AuthUser(object): | |
39 | """ |
|
64 | """ | |
40 | A simple object that handles a mercurial username for authentication |
|
65 | A simple object that handles a mercurial username for authentication | |
41 | """ |
|
66 | """ | |
42 | username = 'None' |
|
67 | username = 'None' | |
43 | is_authenticated = False |
|
68 | is_authenticated = False | |
44 | is_admin = False |
|
69 | is_admin = False | |
45 | permissions = set() |
|
70 | permissions = set() | |
46 | group = set() |
|
71 | group = set() | |
47 |
|
72 | |||
48 | def __init__(self): |
|
73 | def __init__(self): | |
49 | pass |
|
74 | pass | |
50 |
|
75 | |||
51 |
|
76 | |||
52 |
|
77 | |||
53 | def set_available_permissions(config): |
|
78 | def set_available_permissions(config): | |
54 | """ |
|
79 | """ | |
55 | This function will propagate pylons globals with all available defined |
|
80 | This function will propagate pylons globals with all available defined | |
56 | permission given in db. We don't wannt to check each time from db for new |
|
81 | permission given in db. We don't wannt to check each time from db for new | |
57 | permissions since adding a new permission also requires application restart |
|
82 | permissions since adding a new permission also requires application restart | |
58 | ie. to decorate new views with the newly created permission |
|
83 | ie. to decorate new views with the newly created permission | |
59 | @param config: |
|
84 | @param config: | |
60 | """ |
|
85 | """ | |
61 | from pylons_app.model.meta import Session |
|
86 | from pylons_app.model.meta import Session | |
62 | from pylons_app.model.db import Permission |
|
87 | from pylons_app.model.db import Permission | |
63 | logging.info('getting information about all available permissions') |
|
88 | logging.info('getting information about all available permissions') | |
64 | sa = Session() |
|
89 | sa = Session() | |
65 | all_perms = sa.query(Permission).all() |
|
90 | all_perms = sa.query(Permission).all() | |
66 | config['pylons.app_globals'].available_permissions = [x.permission_name for x in all_perms] |
|
91 | config['pylons.app_globals'].available_permissions = [x.permission_name for x in all_perms] | |
67 |
|
92 | |||
68 |
|
93 | |||
69 |
|
94 | |||
70 | #=============================================================================== |
|
95 | #=============================================================================== | |
71 | # DECORATORS |
|
96 | # DECORATORS | |
72 | #=============================================================================== |
|
97 | #=============================================================================== | |
73 | class LoginRequired(object): |
|
98 | class LoginRequired(object): | |
74 | """ |
|
99 | """ | |
75 | Must be logged in to execute this function else redirect to login page |
|
100 | Must be logged in to execute this function else redirect to login page | |
76 | """ |
|
101 | """ | |
77 | def __init__(self): |
|
102 | def __init__(self): | |
78 | pass |
|
103 | pass | |
79 |
|
104 | |||
80 | def __call__(self, func): |
|
105 | def __call__(self, func): | |
81 |
|
106 | |||
82 | @wraps(func) |
|
107 | @wraps(func) | |
83 | def _wrapper(*fargs, **fkwargs): |
|
108 | def _wrapper(*fargs, **fkwargs): | |
84 | user = session.get('hg_app_user', AuthUser()) |
|
109 | user = session.get('hg_app_user', AuthUser()) | |
85 | log.info('Checking login required for user:%s', user.username) |
|
110 | log.info('Checking login required for user:%s', user.username) | |
86 | if user.is_authenticated: |
|
111 | if user.is_authenticated: | |
87 | log.info('user %s is authenticated', user.username) |
|
112 | log.info('user %s is authenticated', user.username) | |
88 | func(*fargs) |
|
113 | func(*fargs) | |
89 | else: |
|
114 | else: | |
90 | logging.info('user %s not authenticated', user.username) |
|
115 | logging.info('user %s not authenticated', user.username) | |
91 | logging.info('redirecting to login page') |
|
116 | logging.info('redirecting to login page') | |
92 | return redirect(url('login_home')) |
|
117 | return redirect(url('login_home')) | |
93 |
|
118 | |||
94 | return _wrapper |
|
119 | return _wrapper | |
95 |
|
120 | |||
96 | class PermsDecorator(object): |
|
121 | class PermsDecorator(object): | |
97 |
|
122 | |||
98 | def __init__(self, *perms): |
|
123 | def __init__(self, *perms): | |
99 | available_perms = g.available_permissions |
|
124 | available_perms = g.available_permissions | |
100 | for perm in perms: |
|
125 | for perm in perms: | |
101 | if perm not in available_perms: |
|
126 | if perm not in available_perms: | |
102 | raise Exception("'%s' permission in not defined" % perm) |
|
127 | raise Exception("'%s' permission in not defined" % perm) | |
103 | self.required_perms = set(perms) |
|
128 | self.required_perms = set(perms) | |
104 | self.user_perms = set([])#propagate this list from somewhere. |
|
129 | self.user_perms = set([])#propagate this list from somewhere. | |
105 |
|
130 | |||
106 | def __call__(self, func): |
|
131 | def __call__(self, func): | |
107 | @wraps(func) |
|
132 | @wraps(func) | |
108 | def _wrapper(*args, **kwargs): |
|
133 | def _wrapper(*args, **kwargs): | |
109 | logging.info('checking %s permissions %s for %s', |
|
134 | logging.info('checking %s permissions %s for %s', | |
110 | self.__class__.__name__[-3:], self.required_perms, func.__name__) |
|
135 | self.__class__.__name__[-3:], self.required_perms, func.__name__) | |
111 |
|
136 | |||
112 | if self.check_permissions(): |
|
137 | if self.check_permissions(): | |
113 | logging.info('Permission granted for %s', func.__name__) |
|
138 | logging.info('Permission granted for %s', func.__name__) | |
114 | return func(*args, **kwargs) |
|
139 | return func(*args, **kwargs) | |
115 |
|
140 | |||
116 | else: |
|
141 | else: | |
117 | logging.warning('Permission denied for %s', func.__name__) |
|
142 | logging.warning('Permission denied for %s', func.__name__) | |
118 | #redirect with forbidden ret code |
|
143 | #redirect with forbidden ret code | |
119 | return redirect(url('access_denied'), 403) |
|
144 | return redirect(url('access_denied'), 403) | |
120 | return _wrapper |
|
145 | return _wrapper | |
121 |
|
146 | |||
122 |
|
147 | |||
123 | def check_permissions(self): |
|
148 | def check_permissions(self): | |
124 | """ |
|
149 | """ | |
125 | Dummy function for overiding |
|
150 | Dummy function for overiding | |
126 | """ |
|
151 | """ | |
127 | raise Exception('You have to write this function in child class') |
|
152 | raise Exception('You have to write this function in child class') | |
128 |
|
153 | |||
129 | class CheckPermissionAll(PermsDecorator): |
|
154 | class CheckPermissionAll(PermsDecorator): | |
130 | """ |
|
155 | """ | |
131 | Checks for access permission for all given predicates. All of them have to |
|
156 | Checks for access permission for all given predicates. All of them have to | |
132 | be meet in order to fulfill the request |
|
157 | be meet in order to fulfill the request | |
133 | """ |
|
158 | """ | |
134 |
|
159 | |||
135 | def check_permissions(self): |
|
160 | def check_permissions(self): | |
136 | if self.required_perms.issubset(self.user_perms): |
|
161 | if self.required_perms.issubset(self.user_perms): | |
137 | return True |
|
162 | return True | |
138 | return False |
|
163 | return False | |
139 |
|
164 | |||
140 |
|
165 | |||
141 | class CheckPermissionAny(PermsDecorator): |
|
166 | class CheckPermissionAny(PermsDecorator): | |
142 | """ |
|
167 | """ | |
143 | Checks for access permission for any of given predicates. In order to |
|
168 | Checks for access permission for any of given predicates. In order to | |
144 | fulfill the request any of predicates must be meet |
|
169 | fulfill the request any of predicates must be meet | |
145 | """ |
|
170 | """ | |
146 |
|
171 | |||
147 | def check_permissions(self): |
|
172 | def check_permissions(self): | |
148 | if self.required_perms.intersection(self.user_perms): |
|
173 | if self.required_perms.intersection(self.user_perms): | |
149 | return True |
|
174 | return True | |
150 | return False |
|
175 | return False | |
151 |
|
176 | |||
152 |
|
177 | |||
153 |
|
178 |
@@ -1,88 +1,114 b'' | |||||
1 | '''BACKUP MANAGER''' |
|
1 | #!/usr/bin/env python | |
|
2 | # encoding: utf-8 | |||
|
3 | # mercurial repository backup manager | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | ||||
|
21 | """ | |||
|
22 | Created on Feb 28, 2010 | |||
|
23 | Mercurial repositories backup manager | |||
|
24 | @author: marcink | |||
|
25 | """ | |||
|
26 | ||||
|
27 | ||||
2 | import logging |
|
28 | import logging | |
3 | from mercurial import config |
|
29 | from mercurial import config | |
4 | import tarfile |
|
30 | import tarfile | |
5 | import os |
|
31 | import os | |
6 | import datetime |
|
32 | import datetime | |
7 | import sys |
|
33 | import sys | |
8 | import subprocess |
|
34 | import subprocess | |
9 | logging.basicConfig(level=logging.DEBUG, |
|
35 | logging.basicConfig(level=logging.DEBUG, | |
10 | format="%(asctime)s %(levelname)-5.5s %(message)s") |
|
36 | format="%(asctime)s %(levelname)-5.5s %(message)s") | |
11 |
|
37 | |||
12 | class BackupManager(object): |
|
38 | class BackupManager(object): | |
13 | def __init__(self, id_rsa_path, repo_conf): |
|
39 | def __init__(self, id_rsa_path, repo_conf): | |
14 | self.repos_path = None |
|
40 | self.repos_path = None | |
15 | self.backup_file_name = None |
|
41 | self.backup_file_name = None | |
16 | self.id_rsa_path = id_rsa_path |
|
42 | self.id_rsa_path = id_rsa_path | |
17 | self.check_id_rsa() |
|
43 | self.check_id_rsa() | |
18 | cur_dir = os.path.realpath(__file__) |
|
44 | cur_dir = os.path.realpath(__file__) | |
19 | dn = os.path.dirname |
|
45 | dn = os.path.dirname | |
20 | self.backup_file_path = os.path.join(dn(dn(dn(cur_dir))), 'data') |
|
46 | self.backup_file_path = os.path.join(dn(dn(dn(cur_dir))), 'data') | |
21 | cfg = config.config() |
|
47 | cfg = config.config() | |
22 | try: |
|
48 | try: | |
23 | cfg.read(os.path.join(dn(dn(dn(cur_dir))), repo_conf)) |
|
49 | cfg.read(os.path.join(dn(dn(dn(cur_dir))), repo_conf)) | |
24 | except IOError: |
|
50 | except IOError: | |
25 | logging.error('Could not read %s', repo_conf) |
|
51 | logging.error('Could not read %s', repo_conf) | |
26 | sys.exit() |
|
52 | sys.exit() | |
27 | self.set_repos_path(cfg.items('paths')) |
|
53 | self.set_repos_path(cfg.items('paths')) | |
28 | logging.info('starting backup for %s', self.repos_path) |
|
54 | logging.info('starting backup for %s', self.repos_path) | |
29 | logging.info('backup target %s', self.backup_file_path) |
|
55 | logging.info('backup target %s', self.backup_file_path) | |
30 |
|
56 | |||
31 | if not os.path.isdir(self.repos_path): |
|
57 | if not os.path.isdir(self.repos_path): | |
32 | raise Exception('Not a valid directory in %s' % self.repos_path) |
|
58 | raise Exception('Not a valid directory in %s' % self.repos_path) | |
33 |
|
59 | |||
34 | def check_id_rsa(self): |
|
60 | def check_id_rsa(self): | |
35 | if not os.path.isfile(self.id_rsa_path): |
|
61 | if not os.path.isfile(self.id_rsa_path): | |
36 | logging.error('Could not load id_rsa key file in %s', |
|
62 | logging.error('Could not load id_rsa key file in %s', | |
37 | self.id_rsa_path) |
|
63 | self.id_rsa_path) | |
38 | sys.exit() |
|
64 | sys.exit() | |
39 |
|
65 | |||
40 | def set_repos_path(self, paths): |
|
66 | def set_repos_path(self, paths): | |
41 | repos_path = paths[0][1].split('/') |
|
67 | repos_path = paths[0][1].split('/') | |
42 | if repos_path[-1] in ['*', '**']: |
|
68 | if repos_path[-1] in ['*', '**']: | |
43 | repos_path = repos_path[:-1] |
|
69 | repos_path = repos_path[:-1] | |
44 | if repos_path[0] != '/': |
|
70 | if repos_path[0] != '/': | |
45 | repos_path[0] = '/' |
|
71 | repos_path[0] = '/' | |
46 | self.repos_path = os.path.join(*repos_path) |
|
72 | self.repos_path = os.path.join(*repos_path) | |
47 |
|
73 | |||
48 | def backup_repos(self): |
|
74 | def backup_repos(self): | |
49 | today = datetime.datetime.now().weekday() + 1 |
|
75 | today = datetime.datetime.now().weekday() + 1 | |
50 | self.backup_file_name = "mercurial_repos.%s.tar.gz" % today |
|
76 | self.backup_file_name = "mercurial_repos.%s.tar.gz" % today | |
51 | bckp_file = os.path.join(self.backup_file_path, self.backup_file_name) |
|
77 | bckp_file = os.path.join(self.backup_file_path, self.backup_file_name) | |
52 | tar = tarfile.open(bckp_file, "w:gz") |
|
78 | tar = tarfile.open(bckp_file, "w:gz") | |
53 |
|
79 | |||
54 | for dir_name in os.listdir(self.repos_path): |
|
80 | for dir_name in os.listdir(self.repos_path): | |
55 | logging.info('backing up %s', dir_name) |
|
81 | logging.info('backing up %s', dir_name) | |
56 | tar.add(os.path.join(self.repos_path, dir_name), dir_name) |
|
82 | tar.add(os.path.join(self.repos_path, dir_name), dir_name) | |
57 | tar.close() |
|
83 | tar.close() | |
58 | logging.info('finished backup of mercurial repositories') |
|
84 | logging.info('finished backup of mercurial repositories') | |
59 |
|
85 | |||
60 |
|
86 | |||
61 |
|
87 | |||
62 | def transfer_files(self): |
|
88 | def transfer_files(self): | |
63 | params = { |
|
89 | params = { | |
64 | 'id_rsa_key': self.id_rsa_path, |
|
90 | 'id_rsa_key': self.id_rsa_path, | |
65 | 'backup_file_path':self.backup_file_path, |
|
91 | 'backup_file_path':self.backup_file_path, | |
66 | 'backup_file_name':self.backup_file_name, |
|
92 | 'backup_file_name':self.backup_file_name, | |
67 | } |
|
93 | } | |
68 | cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params, |
|
94 | cmd = ['scp', '-l', '40000', '-i', '%(id_rsa_key)s' % params, | |
69 | '%(backup_file_path)s/%(backup_file_name)s' % params, |
|
95 | '%(backup_file_path)s/%(backup_file_name)s' % params, | |
70 | 'root@192.168.2.102:/backups/mercurial' % params] |
|
96 | 'root@192.168.2.102:/backups/mercurial' % params] | |
71 |
|
97 | |||
72 | subprocess.call(cmd) |
|
98 | subprocess.call(cmd) | |
73 | logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4]) |
|
99 | logging.info('Transfered file %s to %s', self.backup_file_name, cmd[4]) | |
74 |
|
100 | |||
75 |
|
101 | |||
76 | def rm_file(self): |
|
102 | def rm_file(self): | |
77 | logging.info('Removing file %s', self.backup_file_name) |
|
103 | logging.info('Removing file %s', self.backup_file_name) | |
78 | os.remove(os.path.join(self.backup_file_path, self.backup_file_name)) |
|
104 | os.remove(os.path.join(self.backup_file_path, self.backup_file_name)) | |
79 |
|
105 | |||
80 |
|
106 | |||
81 |
|
107 | |||
82 | if __name__ == "__main__": |
|
108 | if __name__ == "__main__": | |
83 | B_MANAGER = BackupManager('/home/pylons/id_rsa', 'repositories.config') |
|
109 | B_MANAGER = BackupManager('/home/pylons/id_rsa', 'repositories.config') | |
84 | B_MANAGER.backup_repos() |
|
110 | B_MANAGER.backup_repos() | |
85 | B_MANAGER.transfer_files() |
|
111 | B_MANAGER.transfer_files() | |
86 | B_MANAGER.rm_file() |
|
112 | B_MANAGER.rm_file() | |
87 |
|
113 | |||
88 |
|
114 |
@@ -1,97 +1,123 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # database managment for hg app | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | ||||
|
21 | """ | |||
|
22 | Created on April 10, 2010 | |||
|
23 | database managment and creation for hg app | |||
|
24 | @author: marcink | |||
|
25 | """ | |||
|
26 | ||||
1 | from os.path import dirname as dn, join as jn |
|
27 | from os.path import dirname as dn, join as jn | |
2 | import os |
|
28 | import os | |
3 | import sys |
|
29 | import sys | |
4 | ROOT = dn(dn(dn(os.path.realpath(__file__)))) |
|
30 | ROOT = dn(dn(dn(os.path.realpath(__file__)))) | |
5 | sys.path.append(ROOT) |
|
31 | sys.path.append(ROOT) | |
6 |
|
32 | |||
7 | from pylons_app.lib.auth import get_crypt_password |
|
33 | from pylons_app.lib.auth import get_crypt_password | |
8 | from pylons_app.model import init_model |
|
34 | from pylons_app.model import init_model | |
9 | from pylons_app.model.db import User, Permission |
|
35 | from pylons_app.model.db import User, Permission | |
10 | from pylons_app.model.meta import Session, Base |
|
36 | from pylons_app.model.meta import Session, Base | |
11 | from sqlalchemy.engine import create_engine |
|
37 | from sqlalchemy.engine import create_engine | |
12 | import logging |
|
38 | import logging | |
13 |
|
39 | |||
14 | log = logging.getLogger('db manage') |
|
40 | log = logging.getLogger('db manage') | |
15 | log.setLevel(logging.DEBUG) |
|
41 | log.setLevel(logging.DEBUG) | |
16 | console_handler = logging.StreamHandler() |
|
42 | console_handler = logging.StreamHandler() | |
17 | console_handler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)03d" |
|
43 | console_handler.setFormatter(logging.Formatter("%(asctime)s.%(msecs)03d" | |
18 | " %(levelname)-5.5s [%(name)s] %(message)s")) |
|
44 | " %(levelname)-5.5s [%(name)s] %(message)s")) | |
19 | log.addHandler(console_handler) |
|
45 | log.addHandler(console_handler) | |
20 |
|
46 | |||
21 | class DbManage(object): |
|
47 | class DbManage(object): | |
22 | def __init__(self, log_sql): |
|
48 | def __init__(self, log_sql): | |
23 | self.dbname = 'hg_app.db' |
|
49 | self.dbname = 'hg_app.db' | |
24 | dburi = 'sqlite:////%s' % jn(ROOT, self.dbname) |
|
50 | dburi = 'sqlite:////%s' % jn(ROOT, self.dbname) | |
25 | engine = create_engine(dburi, echo=log_sql) |
|
51 | engine = create_engine(dburi, echo=log_sql) | |
26 | init_model(engine) |
|
52 | init_model(engine) | |
27 | self.sa = Session() |
|
53 | self.sa = Session() | |
28 | self.db_exists = False |
|
54 | self.db_exists = False | |
29 |
|
55 | |||
30 | def check_for_db(self, override): |
|
56 | def check_for_db(self, override): | |
31 | log.info('checking for exisiting db') |
|
57 | log.info('checking for exisiting db') | |
32 | if os.path.isfile(jn(ROOT, self.dbname)): |
|
58 | if os.path.isfile(jn(ROOT, self.dbname)): | |
33 | self.db_exists = True |
|
59 | self.db_exists = True | |
34 | log.info('database exisist') |
|
60 | log.info('database exisist') | |
35 | if not override: |
|
61 | if not override: | |
36 | raise Exception('database already exists') |
|
62 | raise Exception('database already exists') | |
37 |
|
63 | |||
38 | def create_tables(self, override=False): |
|
64 | def create_tables(self, override=False): | |
39 | """ |
|
65 | """ | |
40 | Create a auth database |
|
66 | Create a auth database | |
41 | """ |
|
67 | """ | |
42 | self.check_for_db(override) |
|
68 | self.check_for_db(override) | |
43 | if override: |
|
69 | if override: | |
44 | log.info("database exisist and it's going to be destroyed") |
|
70 | log.info("database exisist and it's going to be destroyed") | |
45 | if self.db_exists: |
|
71 | if self.db_exists: | |
46 | os.remove(jn(ROOT, self.dbname)) |
|
72 | os.remove(jn(ROOT, self.dbname)) | |
47 | Base.metadata.create_all(checkfirst=override) |
|
73 | Base.metadata.create_all(checkfirst=override) | |
48 | log.info('Created tables for %s', self.dbname) |
|
74 | log.info('Created tables for %s', self.dbname) | |
49 |
|
75 | |||
50 | def admin_prompt(self): |
|
76 | def admin_prompt(self): | |
51 | import getpass |
|
77 | import getpass | |
52 | username = raw_input('Specify admin username:') |
|
78 | username = raw_input('Specify admin username:') | |
53 | password = getpass.getpass('Specify admin password:') |
|
79 | password = getpass.getpass('Specify admin password:') | |
54 | self.create_user(username, password, True) |
|
80 | self.create_user(username, password, True) | |
55 |
|
81 | |||
56 | def create_user(self, username, password, admin=False): |
|
82 | def create_user(self, username, password, admin=False): | |
57 | log.info('creating administrator user %s', username) |
|
83 | log.info('creating administrator user %s', username) | |
58 |
|
84 | |||
59 | new_user = User() |
|
85 | new_user = User() | |
60 | new_user.username = username |
|
86 | new_user.username = username | |
61 | new_user.password = get_crypt_password(password) |
|
87 | new_user.password = get_crypt_password(password) | |
62 | new_user.admin = admin |
|
88 | new_user.admin = admin | |
63 | new_user.active = True |
|
89 | new_user.active = True | |
64 |
|
90 | |||
65 | try: |
|
91 | try: | |
66 | self.sa.add(new_user) |
|
92 | self.sa.add(new_user) | |
67 | self.sa.commit() |
|
93 | self.sa.commit() | |
68 | except: |
|
94 | except: | |
69 | self.sa.rollback() |
|
95 | self.sa.rollback() | |
70 | raise |
|
96 | raise | |
71 |
|
97 | |||
72 | def create_permissions(self): |
|
98 | def create_permissions(self): | |
73 | #module.(access|create|change|delete)_[name] |
|
99 | #module.(access|create|change|delete)_[name] | |
74 | perms = [('admin.access_home', 'Access to admin user view'), |
|
100 | perms = [('admin.access_home', 'Access to admin user view'), | |
75 |
|
101 | |||
76 | ] |
|
102 | ] | |
77 |
|
103 | |||
78 | for p in perms: |
|
104 | for p in perms: | |
79 | new_perm = Permission() |
|
105 | new_perm = Permission() | |
80 | new_perm.permission_name = p[0] |
|
106 | new_perm.permission_name = p[0] | |
81 | new_perm.permission_longname = p[1] |
|
107 | new_perm.permission_longname = p[1] | |
82 | try: |
|
108 | try: | |
83 | self.sa.add(new_perm) |
|
109 | self.sa.add(new_perm) | |
84 | self.sa.commit() |
|
110 | self.sa.commit() | |
85 | except: |
|
111 | except: | |
86 | self.sa.rollback() |
|
112 | self.sa.rollback() | |
87 | raise |
|
113 | raise | |
88 |
|
114 | |||
89 |
|
115 | |||
90 |
|
116 | |||
91 | if __name__ == '__main__': |
|
117 | if __name__ == '__main__': | |
92 | dbmanage = DbManage(log_sql=True) |
|
118 | dbmanage = DbManage(log_sql=True) | |
93 | dbmanage.create_tables(override=True) |
|
119 | dbmanage.create_tables(override=True) | |
94 | dbmanage.admin_prompt() |
|
120 | dbmanage.admin_prompt() | |
95 | dbmanage.create_permissions() |
|
121 | dbmanage.create_permissions() | |
96 |
|
122 | |||
97 |
|
123 |
@@ -1,23 +1,49 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # simple filters for hg apps html templates | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | ||||
|
21 | """ | |||
|
22 | Created on April 12, 2010 | |||
|
23 | simple filters for hg apps html templates | |||
|
24 | @author: marcink | |||
|
25 | """ | |||
|
26 | ||||
1 | from mercurial import util |
|
27 | from mercurial import util | |
2 | from mercurial.templatefilters import age as _age, person as _person |
|
28 | from mercurial.templatefilters import age as _age, person as _person | |
3 | from string import punctuation |
|
29 | from string import punctuation | |
4 |
|
30 | |||
5 | def clean_repo(repo_name): |
|
31 | def clean_repo(repo_name): | |
6 | for x in punctuation: |
|
32 | for x in punctuation: | |
7 | if x != '_': |
|
33 | if x != '_': | |
8 | repo_name = repo_name.replace(x, '') |
|
34 | repo_name = repo_name.replace(x, '') | |
9 | repo_name = repo_name.lower().strip() |
|
35 | repo_name = repo_name.lower().strip() | |
10 | return repo_name.replace(' ', '_') |
|
36 | return repo_name.replace(' ', '_') | |
11 |
|
37 | |||
12 | age = lambda x:_age(x) |
|
38 | age = lambda x:_age(x) | |
13 | capitalize = lambda x: x.capitalize() |
|
39 | capitalize = lambda x: x.capitalize() | |
14 | date = lambda x: util.datestr(x) |
|
40 | date = lambda x: util.datestr(x) | |
15 | email = util.email |
|
41 | email = util.email | |
16 | person = lambda x: _person(x) |
|
42 | person = lambda x: _person(x) | |
17 | hgdate = lambda x: "%d %d" % x |
|
43 | hgdate = lambda x: "%d %d" % x | |
18 | isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2') |
|
44 | isodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M %1%2') | |
19 | isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2') |
|
45 | isodatesec = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2') | |
20 | localdate = lambda x: (x[0], util.makedate()[1]) |
|
46 | localdate = lambda x: (x[0], util.makedate()[1]) | |
21 | rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2") |
|
47 | rfc822date = lambda x: util.datestr(x, "%a, %d %b %Y %H:%M:%S %1%2") | |
22 | rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2") |
|
48 | rfc3339date = lambda x: util.datestr(x, "%Y-%m-%dT%H:%M:%S%1:%2") | |
23 | time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2") |
|
49 | time_ago = lambda x: util.datestr(_age(x), "%a, %d %b %Y %H:%M:%S %1%2") |
@@ -1,21 +1,47 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # middleware to handle https correctly | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | ||||
|
21 | """ | |||
|
22 | Created on May 23, 2010 | |||
|
23 | ||||
|
24 | @author: marcink | |||
|
25 | """ | |||
|
26 | ||||
1 | class HttpsFixup(object): |
|
27 | class HttpsFixup(object): | |
2 | def __init__(self, app): |
|
28 | def __init__(self, app): | |
3 | self.application = app |
|
29 | self.application = app | |
4 |
|
30 | |||
5 | def __call__(self, environ, start_response): |
|
31 | def __call__(self, environ, start_response): | |
6 | self.__fixup(environ) |
|
32 | self.__fixup(environ) | |
7 | return self.application(environ, start_response) |
|
33 | return self.application(environ, start_response) | |
8 |
|
34 | |||
9 |
|
35 | |||
10 | def __fixup(self, environ): |
|
36 | def __fixup(self, environ): | |
11 | """Function to fixup the environ as needed. In order to use this |
|
37 | """Function to fixup the environ as needed. In order to use this | |
12 | middleware you should set this header inside your |
|
38 | middleware you should set this header inside your | |
13 | proxy ie. nginx, apache etc. |
|
39 | proxy ie. nginx, apache etc. | |
14 | """ |
|
40 | """ | |
15 | proto = environ.get('HTTP_X_URL_SCHEME') |
|
41 | proto = environ.get('HTTP_X_URL_SCHEME') | |
16 |
|
42 | |||
17 | if proto == 'https': |
|
43 | if proto == 'https': | |
18 | environ['wsgi.url_scheme'] = proto |
|
44 | environ['wsgi.url_scheme'] = proto | |
19 | else: |
|
45 | else: | |
20 | environ['wsgi.url_scheme'] = 'http' |
|
46 | environ['wsgi.url_scheme'] = 'http' | |
21 | return None |
|
47 | return None |
@@ -1,138 +1,153 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # |
|
3 | # middleware to handle mercurial api calls | |
4 | # Copyright (c) 2010 marcink. All rights reserved. |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 | # |
|
5 | ||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | ||||
6 | """ |
|
21 | """ | |
7 | Created on 2010-04-28 |
|
22 | Created on 2010-04-28 | |
8 |
|
23 | |||
9 | @author: marcink |
|
24 | @author: marcink | |
10 | SimpleHG middleware for handling mercurial protocol request (push/clone etc.) |
|
25 | SimpleHG middleware for handling mercurial protocol request (push/clone etc.) | |
11 | It's implemented with basic auth function |
|
26 | It's implemented with basic auth function | |
12 | """ |
|
27 | """ | |
13 | from datetime import datetime |
|
28 | from datetime import datetime | |
14 | from mercurial.hgweb import hgweb |
|
29 | from mercurial.hgweb import hgweb | |
15 | from mercurial.hgweb.request import wsgiapplication |
|
30 | from mercurial.hgweb.request import wsgiapplication | |
16 | from paste.auth.basic import AuthBasicAuthenticator |
|
31 | from paste.auth.basic import AuthBasicAuthenticator | |
17 | from paste.httpheaders import REMOTE_USER, AUTH_TYPE |
|
32 | from paste.httpheaders import REMOTE_USER, AUTH_TYPE | |
18 | from pylons_app.lib.auth import authfunc |
|
33 | from pylons_app.lib.auth import authfunc | |
19 | from pylons_app.lib.utils import is_mercurial, make_ui, invalidate_cache |
|
34 | from pylons_app.lib.utils import is_mercurial, make_ui, invalidate_cache | |
20 | from pylons_app.model import meta |
|
35 | from pylons_app.model import meta | |
21 | from pylons_app.model.db import UserLog, User |
|
36 | from pylons_app.model.db import UserLog, User | |
22 | from webob.exc import HTTPNotFound |
|
37 | from webob.exc import HTTPNotFound | |
23 | import logging |
|
38 | import logging | |
24 | import os |
|
39 | import os | |
25 | log = logging.getLogger(__name__) |
|
40 | log = logging.getLogger(__name__) | |
26 |
|
41 | |||
27 | class SimpleHg(object): |
|
42 | class SimpleHg(object): | |
28 |
|
43 | |||
29 | def __init__(self, application, config): |
|
44 | def __init__(self, application, config): | |
30 | self.application = application |
|
45 | self.application = application | |
31 | self.config = config |
|
46 | self.config = config | |
32 | #authenticate this mercurial request using |
|
47 | #authenticate this mercurial request using | |
33 | realm = '%s %s' % (config['hg_app_name'], 'mercurial repository') |
|
48 | realm = '%s %s' % (config['hg_app_name'], 'mercurial repository') | |
34 | self.authenticate = AuthBasicAuthenticator(realm, authfunc) |
|
49 | self.authenticate = AuthBasicAuthenticator(realm, authfunc) | |
35 |
|
50 | |||
36 | def __call__(self, environ, start_response): |
|
51 | def __call__(self, environ, start_response): | |
37 | if not is_mercurial(environ): |
|
52 | if not is_mercurial(environ): | |
38 | return self.application(environ, start_response) |
|
53 | return self.application(environ, start_response) | |
39 | else: |
|
54 | else: | |
40 | #=================================================================== |
|
55 | #=================================================================== | |
41 | # AUTHENTICATE THIS MERCURIAL REQUEST |
|
56 | # AUTHENTICATE THIS MERCURIAL REQUEST | |
42 | #=================================================================== |
|
57 | #=================================================================== | |
43 | username = REMOTE_USER(environ) |
|
58 | username = REMOTE_USER(environ) | |
44 | if not username: |
|
59 | if not username: | |
45 | result = self.authenticate(environ) |
|
60 | result = self.authenticate(environ) | |
46 | if isinstance(result, str): |
|
61 | if isinstance(result, str): | |
47 | AUTH_TYPE.update(environ, 'basic') |
|
62 | AUTH_TYPE.update(environ, 'basic') | |
48 | REMOTE_USER.update(environ, result) |
|
63 | REMOTE_USER.update(environ, result) | |
49 | else: |
|
64 | else: | |
50 | return result.wsgi_application(environ, start_response) |
|
65 | return result.wsgi_application(environ, start_response) | |
51 |
|
66 | |||
52 | try: |
|
67 | try: | |
53 | repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:]) |
|
68 | repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:]) | |
54 | except Exception as e: |
|
69 | except Exception as e: | |
55 | log.error(e) |
|
70 | log.error(e) | |
56 | return HTTPNotFound()(environ, start_response) |
|
71 | return HTTPNotFound()(environ, start_response) | |
57 |
|
72 | |||
58 | #since we wrap into hgweb, just reset the path |
|
73 | #since we wrap into hgweb, just reset the path | |
59 | environ['PATH_INFO'] = '/' |
|
74 | environ['PATH_INFO'] = '/' | |
60 | self.baseui = make_ui(self.config['hg_app_repo_conf']) |
|
75 | self.baseui = make_ui(self.config['hg_app_repo_conf']) | |
61 | self.basepath = self.baseui.configitems('paths')[0][1]\ |
|
76 | self.basepath = self.baseui.configitems('paths')[0][1]\ | |
62 | .replace('*', '') |
|
77 | .replace('*', '') | |
63 | self.repo_path = os.path.join(self.basepath, repo_name) |
|
78 | self.repo_path = os.path.join(self.basepath, repo_name) | |
64 | try: |
|
79 | try: | |
65 | app = wsgiapplication(self.__make_app) |
|
80 | app = wsgiapplication(self.__make_app) | |
66 | except Exception as e: |
|
81 | except Exception as e: | |
67 | log.error(e) |
|
82 | log.error(e) | |
68 | return HTTPNotFound()(environ, start_response) |
|
83 | return HTTPNotFound()(environ, start_response) | |
69 | action = self.__get_action(environ) |
|
84 | action = self.__get_action(environ) | |
70 | #invalidate cache on push |
|
85 | #invalidate cache on push | |
71 | if action == 'push': |
|
86 | if action == 'push': | |
72 | self.__invalidate_cache(repo_name) |
|
87 | self.__invalidate_cache(repo_name) | |
73 |
|
88 | |||
74 | if action: |
|
89 | if action: | |
75 | username = self.__get_environ_user(environ) |
|
90 | username = self.__get_environ_user(environ) | |
76 | self.__log_user_action(username, action, repo_name) |
|
91 | self.__log_user_action(username, action, repo_name) | |
77 |
|
92 | |||
78 | return app(environ, start_response) |
|
93 | return app(environ, start_response) | |
79 |
|
94 | |||
80 | def __make_app(self): |
|
95 | def __make_app(self): | |
81 | hgserve = hgweb(self.repo_path) |
|
96 | hgserve = hgweb(self.repo_path) | |
82 | return self.__load_web_settings(hgserve) |
|
97 | return self.__load_web_settings(hgserve) | |
83 |
|
98 | |||
84 | def __get_environ_user(self, environ): |
|
99 | def __get_environ_user(self, environ): | |
85 | return environ.get('REMOTE_USER') |
|
100 | return environ.get('REMOTE_USER') | |
86 |
|
101 | |||
87 | def __get_action(self, environ): |
|
102 | def __get_action(self, environ): | |
88 | """ |
|
103 | """ | |
89 | Maps mercurial request commands into a pull or push command. |
|
104 | Maps mercurial request commands into a pull or push command. | |
90 | @param environ: |
|
105 | @param environ: | |
91 | """ |
|
106 | """ | |
92 | mapping = { |
|
107 | mapping = { | |
93 | 'changegroup': 'pull', |
|
108 | 'changegroup': 'pull', | |
94 | 'changegroupsubset': 'pull', |
|
109 | 'changegroupsubset': 'pull', | |
95 | 'unbundle': 'push', |
|
110 | 'unbundle': 'push', | |
96 | 'stream_out': 'pull', |
|
111 | 'stream_out': 'pull', | |
97 | } |
|
112 | } | |
98 | for qry in environ['QUERY_STRING'].split('&'): |
|
113 | for qry in environ['QUERY_STRING'].split('&'): | |
99 | if qry.startswith('cmd'): |
|
114 | if qry.startswith('cmd'): | |
100 | cmd = qry.split('=')[-1] |
|
115 | cmd = qry.split('=')[-1] | |
101 | if mapping.has_key(cmd): |
|
116 | if mapping.has_key(cmd): | |
102 | return mapping[cmd] |
|
117 | return mapping[cmd] | |
103 |
|
118 | |||
104 | def __log_user_action(self, username, action, repo): |
|
119 | def __log_user_action(self, username, action, repo): | |
105 | sa = meta.Session |
|
120 | sa = meta.Session | |
106 | try: |
|
121 | try: | |
107 | user = sa.query(User).filter(User.username == username).one() |
|
122 | user = sa.query(User).filter(User.username == username).one() | |
108 | user_log = UserLog() |
|
123 | user_log = UserLog() | |
109 | user_log.user_id = user.user_id |
|
124 | user_log.user_id = user.user_id | |
110 | user_log.action = action |
|
125 | user_log.action = action | |
111 | user_log.repository = repo.replace('/', '') |
|
126 | user_log.repository = repo.replace('/', '') | |
112 | user_log.action_date = datetime.now() |
|
127 | user_log.action_date = datetime.now() | |
113 | sa.add(user_log) |
|
128 | sa.add(user_log) | |
114 | sa.commit() |
|
129 | sa.commit() | |
115 | log.info('Adding user %s, action %s on %s', |
|
130 | log.info('Adding user %s, action %s on %s', | |
116 | username, action, repo) |
|
131 | username, action, repo) | |
117 | except Exception as e: |
|
132 | except Exception as e: | |
118 | sa.rollback() |
|
133 | sa.rollback() | |
119 | log.error('could not log user action:%s', str(e)) |
|
134 | log.error('could not log user action:%s', str(e)) | |
120 |
|
135 | |||
121 | def __invalidate_cache(self, repo_name): |
|
136 | def __invalidate_cache(self, repo_name): | |
122 | """we know that some change was made to repositories and we should |
|
137 | """we know that some change was made to repositories and we should | |
123 | invalidate the cache to see the changes right away but only for |
|
138 | invalidate the cache to see the changes right away but only for | |
124 | push requests""" |
|
139 | push requests""" | |
125 | invalidate_cache('cached_repo_list') |
|
140 | invalidate_cache('cached_repo_list') | |
126 | invalidate_cache('full_changelog', repo_name) |
|
141 | invalidate_cache('full_changelog', repo_name) | |
127 |
|
142 | |||
128 |
|
143 | |||
129 | def __load_web_settings(self, hgserve): |
|
144 | def __load_web_settings(self, hgserve): | |
130 | repoui = make_ui(os.path.join(self.repo_path, '.hg', 'hgrc'), False) |
|
145 | repoui = make_ui(os.path.join(self.repo_path, '.hg', 'hgrc'), False) | |
131 | #set the global ui for hgserve |
|
146 | #set the global ui for hgserve | |
132 | hgserve.repo.ui = self.baseui |
|
147 | hgserve.repo.ui = self.baseui | |
133 |
|
148 | |||
134 | if repoui: |
|
149 | if repoui: | |
135 | #set the repository based config |
|
150 | #set the repository based config | |
136 | hgserve.repo.ui = repoui |
|
151 | hgserve.repo.ui = repoui | |
137 |
|
152 | |||
138 | return hgserve |
|
153 | return hgserve |
@@ -1,134 +1,160 b'' | |||||
|
1 | #!/usr/bin/env python | |||
|
2 | # encoding: utf-8 | |||
|
3 | # Utilities for hg app | |||
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |||
|
5 | ||||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | ||||
|
21 | """ | |||
|
22 | Created on April 18, 2010 | |||
|
23 | Utilities for hg app | |||
|
24 | @author: marcink | |||
|
25 | """ | |||
|
26 | ||||
1 | import os |
|
27 | import os | |
2 | import logging |
|
28 | import logging | |
3 | from mercurial import ui, config, hg |
|
29 | from mercurial import ui, config, hg | |
4 | from mercurial.error import RepoError |
|
30 | from mercurial.error import RepoError | |
5 | log = logging.getLogger(__name__) |
|
31 | log = logging.getLogger(__name__) | |
6 |
|
32 | |||
7 |
|
33 | |||
8 | def get_repo_slug(request): |
|
34 | def get_repo_slug(request): | |
9 | return request.environ['pylons.routes_dict'].get('repo_name') |
|
35 | return request.environ['pylons.routes_dict'].get('repo_name') | |
10 |
|
36 | |||
11 | def is_mercurial(environ): |
|
37 | def is_mercurial(environ): | |
12 | """ |
|
38 | """ | |
13 | Returns True if request's target is mercurial server - header |
|
39 | Returns True if request's target is mercurial server - header | |
14 | ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``. |
|
40 | ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``. | |
15 | """ |
|
41 | """ | |
16 | http_accept = environ.get('HTTP_ACCEPT') |
|
42 | http_accept = environ.get('HTTP_ACCEPT') | |
17 | if http_accept and http_accept.startswith('application/mercurial'): |
|
43 | if http_accept and http_accept.startswith('application/mercurial'): | |
18 | return True |
|
44 | return True | |
19 | return False |
|
45 | return False | |
20 |
|
46 | |||
21 | def check_repo_dir(paths): |
|
47 | def check_repo_dir(paths): | |
22 | repos_path = paths[0][1].split('/') |
|
48 | repos_path = paths[0][1].split('/') | |
23 | if repos_path[-1] in ['*', '**']: |
|
49 | if repos_path[-1] in ['*', '**']: | |
24 | repos_path = repos_path[:-1] |
|
50 | repos_path = repos_path[:-1] | |
25 | if repos_path[0] != '/': |
|
51 | if repos_path[0] != '/': | |
26 | repos_path[0] = '/' |
|
52 | repos_path[0] = '/' | |
27 | if not os.path.isdir(os.path.join(*repos_path)): |
|
53 | if not os.path.isdir(os.path.join(*repos_path)): | |
28 | raise Exception('Not a valid repository in %s' % paths[0][1]) |
|
54 | raise Exception('Not a valid repository in %s' % paths[0][1]) | |
29 |
|
55 | |||
30 | def check_repo(repo_name, base_path): |
|
56 | def check_repo(repo_name, base_path): | |
31 |
|
57 | |||
32 | repo_path = os.path.join(base_path, repo_name) |
|
58 | repo_path = os.path.join(base_path, repo_name) | |
33 |
|
59 | |||
34 | try: |
|
60 | try: | |
35 | r = hg.repository(ui.ui(), repo_path) |
|
61 | r = hg.repository(ui.ui(), repo_path) | |
36 | hg.verify(r) |
|
62 | hg.verify(r) | |
37 | #here we hnow that repo exists it was verified |
|
63 | #here we hnow that repo exists it was verified | |
38 | log.info('%s repo is already created', repo_name) |
|
64 | log.info('%s repo is already created', repo_name) | |
39 | return False |
|
65 | return False | |
40 | #raise Exception('Repo exists') |
|
66 | #raise Exception('Repo exists') | |
41 | except RepoError: |
|
67 | except RepoError: | |
42 | log.info('%s repo is free for creation', repo_name) |
|
68 | log.info('%s repo is free for creation', repo_name) | |
43 | #it means that there is no valid repo there... |
|
69 | #it means that there is no valid repo there... | |
44 | return True |
|
70 | return True | |
45 |
|
71 | |||
46 | def make_ui(path=None, checkpaths=True): |
|
72 | def make_ui(path=None, checkpaths=True): | |
47 | """ |
|
73 | """ | |
48 | A funcion that will read python rc files and make an ui from read options |
|
74 | A funcion that will read python rc files and make an ui from read options | |
49 |
|
75 | |||
50 | @param path: path to mercurial config file |
|
76 | @param path: path to mercurial config file | |
51 | """ |
|
77 | """ | |
52 | if not path: |
|
78 | if not path: | |
53 | log.error('repos config path is empty !') |
|
79 | log.error('repos config path is empty !') | |
54 |
|
80 | |||
55 | if not os.path.isfile(path): |
|
81 | if not os.path.isfile(path): | |
56 | log.warning('Unable to read config file %s' % path) |
|
82 | log.warning('Unable to read config file %s' % path) | |
57 | return False |
|
83 | return False | |
58 | #propagated from mercurial documentation |
|
84 | #propagated from mercurial documentation | |
59 | sections = [ |
|
85 | sections = [ | |
60 | 'alias', |
|
86 | 'alias', | |
61 | 'auth', |
|
87 | 'auth', | |
62 | 'decode/encode', |
|
88 | 'decode/encode', | |
63 | 'defaults', |
|
89 | 'defaults', | |
64 | 'diff', |
|
90 | 'diff', | |
65 | 'email', |
|
91 | 'email', | |
66 | 'extensions', |
|
92 | 'extensions', | |
67 | 'format', |
|
93 | 'format', | |
68 | 'merge-patterns', |
|
94 | 'merge-patterns', | |
69 | 'merge-tools', |
|
95 | 'merge-tools', | |
70 | 'hooks', |
|
96 | 'hooks', | |
71 | 'http_proxy', |
|
97 | 'http_proxy', | |
72 | 'smtp', |
|
98 | 'smtp', | |
73 | 'patch', |
|
99 | 'patch', | |
74 | 'paths', |
|
100 | 'paths', | |
75 | 'profiling', |
|
101 | 'profiling', | |
76 | 'server', |
|
102 | 'server', | |
77 | 'trusted', |
|
103 | 'trusted', | |
78 | 'ui', |
|
104 | 'ui', | |
79 | 'web', |
|
105 | 'web', | |
80 | ] |
|
106 | ] | |
81 |
|
107 | |||
82 | baseui = ui.ui() |
|
108 | baseui = ui.ui() | |
83 | cfg = config.config() |
|
109 | cfg = config.config() | |
84 | cfg.read(path) |
|
110 | cfg.read(path) | |
85 | if checkpaths:check_repo_dir(cfg.items('paths')) |
|
111 | if checkpaths:check_repo_dir(cfg.items('paths')) | |
86 |
|
112 | |||
87 | for section in sections: |
|
113 | for section in sections: | |
88 | for k, v in cfg.items(section): |
|
114 | for k, v in cfg.items(section): | |
89 | baseui.setconfig(section, k, v) |
|
115 | baseui.setconfig(section, k, v) | |
90 |
|
116 | |||
91 | return baseui |
|
117 | return baseui | |
92 |
|
118 | |||
93 | def invalidate_cache(name, *args): |
|
119 | def invalidate_cache(name, *args): | |
94 | """Invalidates given name cache""" |
|
120 | """Invalidates given name cache""" | |
95 |
|
121 | |||
96 | from beaker.cache import region_invalidate |
|
122 | from beaker.cache import region_invalidate | |
97 | log.info('INVALIDATING CACHE FOR %s', name) |
|
123 | log.info('INVALIDATING CACHE FOR %s', name) | |
98 |
|
124 | |||
99 | """propagate our arguments to make sure invalidation works. First |
|
125 | """propagate our arguments to make sure invalidation works. First | |
100 | argument has to be the name of cached func name give to cache decorator |
|
126 | argument has to be the name of cached func name give to cache decorator | |
101 | without that the invalidation would not work""" |
|
127 | without that the invalidation would not work""" | |
102 | tmp = [name] |
|
128 | tmp = [name] | |
103 | tmp.extend(args) |
|
129 | tmp.extend(args) | |
104 | args = tuple(tmp) |
|
130 | args = tuple(tmp) | |
105 |
|
131 | |||
106 | if name == 'cached_repo_list': |
|
132 | if name == 'cached_repo_list': | |
107 | from pylons_app.model.hg_model import _get_repos_cached |
|
133 | from pylons_app.model.hg_model import _get_repos_cached | |
108 | region_invalidate(_get_repos_cached, None, *args) |
|
134 | region_invalidate(_get_repos_cached, None, *args) | |
109 |
|
135 | |||
110 | if name == 'full_changelog': |
|
136 | if name == 'full_changelog': | |
111 | from pylons_app.model.hg_model import _full_changelog_cached |
|
137 | from pylons_app.model.hg_model import _full_changelog_cached | |
112 | region_invalidate(_full_changelog_cached, None, *args) |
|
138 | region_invalidate(_full_changelog_cached, None, *args) | |
113 |
|
139 | |||
114 | from vcs.backends.base import BaseChangeset |
|
140 | from vcs.backends.base import BaseChangeset | |
115 | from vcs.utils.lazy import LazyProperty |
|
141 | from vcs.utils.lazy import LazyProperty | |
116 | class EmptyChangeset(BaseChangeset): |
|
142 | class EmptyChangeset(BaseChangeset): | |
117 |
|
143 | |||
118 | revision = -1 |
|
144 | revision = -1 | |
119 |
|
145 | |||
120 | @LazyProperty |
|
146 | @LazyProperty | |
121 | def raw_id(self): |
|
147 | def raw_id(self): | |
122 | """ |
|
148 | """ | |
123 | Returns raw string identifing this changeset, useful for web |
|
149 | Returns raw string identifing this changeset, useful for web | |
124 | representation. |
|
150 | representation. | |
125 | """ |
|
151 | """ | |
126 | return '0' * 12 |
|
152 | return '0' * 12 | |
127 |
|
153 | |||
128 |
|
154 | |||
129 | def repo2db_mapper(): |
|
155 | def repo2db_mapper(): | |
130 | """ |
|
156 | """ | |
131 | scann all dirs for .hgdbid |
|
157 | scann all dirs for .hgdbid | |
132 | if some dir doesn't have one generate one. |
|
158 | if some dir doesn't have one generate one. | |
133 | """ |
|
159 | """ | |
134 | pass No newline at end of file |
|
160 | pass |
@@ -1,126 +1,141 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # |
|
3 | # Model for hg app | |
4 | # Copyright (c) 2010 marcink. All rights reserved. |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 | # |
|
5 | ||
6 | ''' |
|
6 | # This program is free software; you can redistribute it and/or | |
7 | Created on Apr 9, 2010 |
|
7 | # modify it under the terms of the GNU General Public License | |
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
8 |
|
20 | |||
|
21 | """ | |||
|
22 | Created on April 9, 2010 | |||
|
23 | Model for hg app | |||
9 | @author: marcink |
|
24 | @author: marcink | |
10 | ''' |
|
25 | """ | |
11 |
|
26 | |||
12 | from beaker.cache import cache_region |
|
27 | from beaker.cache import cache_region | |
13 | from mercurial import ui |
|
28 | from mercurial import ui | |
14 | from mercurial.hgweb.hgwebdir_mod import findrepos |
|
29 | from mercurial.hgweb.hgwebdir_mod import findrepos | |
15 | from pylons import app_globals as g |
|
30 | from pylons import app_globals as g | |
16 | from vcs.exceptions import RepositoryError, VCSError |
|
31 | from vcs.exceptions import RepositoryError, VCSError | |
17 | import logging |
|
32 | import logging | |
18 | import os |
|
33 | import os | |
19 | import sys |
|
34 | import sys | |
20 | log = logging.getLogger(__name__) |
|
35 | log = logging.getLogger(__name__) | |
21 |
|
36 | |||
22 | try: |
|
37 | try: | |
23 | from vcs.backends.hg import MercurialRepository |
|
38 | from vcs.backends.hg import MercurialRepository | |
24 | except ImportError: |
|
39 | except ImportError: | |
25 | sys.stderr.write('You have to import vcs module') |
|
40 | sys.stderr.write('You have to import vcs module') | |
26 | raise Exception('Unable to import vcs') |
|
41 | raise Exception('Unable to import vcs') | |
27 |
|
42 | |||
28 |
|
43 | |||
29 | @cache_region('long_term', 'cached_repo_list') |
|
44 | @cache_region('long_term', 'cached_repo_list') | |
30 | def _get_repos_cached(): |
|
45 | def _get_repos_cached(): | |
31 | """ |
|
46 | """ | |
32 | return cached dict with repos |
|
47 | return cached dict with repos | |
33 | """ |
|
48 | """ | |
34 | return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui) |
|
49 | return HgModel.repo_scan(g.paths[0][0], g.paths[0][1], g.baseui) | |
35 |
|
50 | |||
36 | @cache_region('long_term', 'full_changelog') |
|
51 | @cache_region('long_term', 'full_changelog') | |
37 | def _full_changelog_cached(repo_name): |
|
52 | def _full_changelog_cached(repo_name): | |
38 | log.info('getting full changelog for %s', repo_name) |
|
53 | log.info('getting full changelog for %s', repo_name) | |
39 | return list(reversed(list(HgModel().get_repo(repo_name)))) |
|
54 | return list(reversed(list(HgModel().get_repo(repo_name)))) | |
40 |
|
55 | |||
41 | class HgModel(object): |
|
56 | class HgModel(object): | |
42 | """ |
|
57 | """ | |
43 | Mercurial Model |
|
58 | Mercurial Model | |
44 | """ |
|
59 | """ | |
45 |
|
60 | |||
46 | def __init__(self): |
|
61 | def __init__(self): | |
47 | """ |
|
62 | """ | |
48 | Constructor |
|
63 | Constructor | |
49 | """ |
|
64 | """ | |
50 | pass |
|
65 | pass | |
51 |
|
66 | |||
52 | @staticmethod |
|
67 | @staticmethod | |
53 | def repo_scan(repos_prefix, repos_path, baseui): |
|
68 | def repo_scan(repos_prefix, repos_path, baseui): | |
54 | """ |
|
69 | """ | |
55 | Listing of repositories in given path. This path should not be a |
|
70 | Listing of repositories in given path. This path should not be a | |
56 | repository itself. Return a dictionary of repository objects |
|
71 | repository itself. Return a dictionary of repository objects | |
57 | :param repos_path: path to directory it could take syntax with |
|
72 | :param repos_path: path to directory it could take syntax with | |
58 | * or ** for deep recursive displaying repositories |
|
73 | * or ** for deep recursive displaying repositories | |
59 | """ |
|
74 | """ | |
60 | def check_repo_dir(path): |
|
75 | def check_repo_dir(path): | |
61 | """ |
|
76 | """ | |
62 | Checks the repository |
|
77 | Checks the repository | |
63 | :param path: |
|
78 | :param path: | |
64 | """ |
|
79 | """ | |
65 | repos_path = path.split('/') |
|
80 | repos_path = path.split('/') | |
66 | if repos_path[-1] in ['*', '**']: |
|
81 | if repos_path[-1] in ['*', '**']: | |
67 | repos_path = repos_path[:-1] |
|
82 | repos_path = repos_path[:-1] | |
68 | if repos_path[0] != '/': |
|
83 | if repos_path[0] != '/': | |
69 | repos_path[0] = '/' |
|
84 | repos_path[0] = '/' | |
70 | if not os.path.isdir(os.path.join(*repos_path)): |
|
85 | if not os.path.isdir(os.path.join(*repos_path)): | |
71 | raise RepositoryError('Not a valid repository in %s' % path[0][1]) |
|
86 | raise RepositoryError('Not a valid repository in %s' % path[0][1]) | |
72 | if not repos_path.endswith('*'): |
|
87 | if not repos_path.endswith('*'): | |
73 | raise VCSError('You need to specify * or ** at the end of path ' |
|
88 | raise VCSError('You need to specify * or ** at the end of path ' | |
74 | 'for recursive scanning') |
|
89 | 'for recursive scanning') | |
75 |
|
90 | |||
76 | check_repo_dir(repos_path) |
|
91 | check_repo_dir(repos_path) | |
77 | log.info('scanning for repositories in %s', repos_path) |
|
92 | log.info('scanning for repositories in %s', repos_path) | |
78 | repos = findrepos([(repos_prefix, repos_path)]) |
|
93 | repos = findrepos([(repos_prefix, repos_path)]) | |
79 | if not isinstance(baseui, ui.ui): |
|
94 | if not isinstance(baseui, ui.ui): | |
80 | baseui = ui.ui() |
|
95 | baseui = ui.ui() | |
81 |
|
96 | |||
82 | repos_list = {} |
|
97 | repos_list = {} | |
83 | for name, path in repos: |
|
98 | for name, path in repos: | |
84 | try: |
|
99 | try: | |
85 | #name = name.split('/')[-1] |
|
100 | #name = name.split('/')[-1] | |
86 | if repos_list.has_key(name): |
|
101 | if repos_list.has_key(name): | |
87 | raise RepositoryError('Duplicate repository name %s found in' |
|
102 | raise RepositoryError('Duplicate repository name %s found in' | |
88 | ' %s' % (name, path)) |
|
103 | ' %s' % (name, path)) | |
89 | else: |
|
104 | else: | |
90 | repos_list[name] = MercurialRepository(path, baseui=baseui) |
|
105 | repos_list[name] = MercurialRepository(path, baseui=baseui) | |
91 | repos_list[name].name = name |
|
106 | repos_list[name].name = name | |
92 | except OSError: |
|
107 | except OSError: | |
93 | continue |
|
108 | continue | |
94 | return repos_list |
|
109 | return repos_list | |
95 |
|
110 | |||
96 | def get_repos(self): |
|
111 | def get_repos(self): | |
97 | for name, repo in _get_repos_cached().items(): |
|
112 | for name, repo in _get_repos_cached().items(): | |
98 | if repo._get_hidden(): |
|
113 | if repo._get_hidden(): | |
99 | #skip hidden web repository |
|
114 | #skip hidden web repository | |
100 | continue |
|
115 | continue | |
101 |
|
116 | |||
102 | last_change = repo.last_change |
|
117 | last_change = repo.last_change | |
103 | try: |
|
118 | try: | |
104 | tip = repo.get_changeset('tip') |
|
119 | tip = repo.get_changeset('tip') | |
105 | except RepositoryError: |
|
120 | except RepositoryError: | |
106 | from pylons_app.lib.utils import EmptyChangeset |
|
121 | from pylons_app.lib.utils import EmptyChangeset | |
107 | tip = EmptyChangeset() |
|
122 | tip = EmptyChangeset() | |
108 |
|
123 | |||
109 | tmp_d = {} |
|
124 | tmp_d = {} | |
110 | tmp_d['name'] = repo.name |
|
125 | tmp_d['name'] = repo.name | |
111 | tmp_d['name_sort'] = tmp_d['name'].lower() |
|
126 | tmp_d['name_sort'] = tmp_d['name'].lower() | |
112 | tmp_d['description'] = repo.description |
|
127 | tmp_d['description'] = repo.description | |
113 | tmp_d['description_sort'] = tmp_d['description'] |
|
128 | tmp_d['description_sort'] = tmp_d['description'] | |
114 | tmp_d['last_change'] = last_change |
|
129 | tmp_d['last_change'] = last_change | |
115 | tmp_d['last_change_sort'] = last_change[1] - last_change[0] |
|
130 | tmp_d['last_change_sort'] = last_change[1] - last_change[0] | |
116 | tmp_d['tip'] = tip.raw_id |
|
131 | tmp_d['tip'] = tip.raw_id | |
117 | tmp_d['tip_sort'] = tip.revision |
|
132 | tmp_d['tip_sort'] = tip.revision | |
118 | tmp_d['rev'] = tip.revision |
|
133 | tmp_d['rev'] = tip.revision | |
119 | tmp_d['contact'] = repo.contact |
|
134 | tmp_d['contact'] = repo.contact | |
120 | tmp_d['contact_sort'] = tmp_d['contact'] |
|
135 | tmp_d['contact_sort'] = tmp_d['contact'] | |
121 | tmp_d['repo_archives'] = list(repo._get_archives()) |
|
136 | tmp_d['repo_archives'] = list(repo._get_archives()) | |
122 |
|
137 | |||
123 | yield tmp_d |
|
138 | yield tmp_d | |
124 |
|
139 | |||
125 | def get_repo(self, repo_name): |
|
140 | def get_repo(self, repo_name): | |
126 | return _get_repos_cached()[repo_name] |
|
141 | return _get_repos_cached()[repo_name] |
@@ -1,48 +1,64 b'' | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 | # encoding: utf-8 |
|
2 | # encoding: utf-8 | |
3 | # |
|
3 | # Model for users | |
4 | # Copyright (c) 2010 marcink. All rights reserved. |
|
4 | # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com> | |
5 | # |
|
5 | ||
|
6 | # This program is free software; you can redistribute it and/or | |||
|
7 | # modify it under the terms of the GNU General Public License | |||
|
8 | # as published by the Free Software Foundation; version 2 | |||
|
9 | # of the License or (at your opinion) any later version of the license. | |||
|
10 | # | |||
|
11 | # This program is distributed in the hope that it will be useful, | |||
|
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
14 | # GNU General Public License for more details. | |||
|
15 | # | |||
|
16 | # You should have received a copy of the GNU General Public License | |||
|
17 | # along with this program; if not, write to the Free Software | |||
|
18 | # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, | |||
|
19 | # MA 02110-1301, USA. | |||
|
20 | ||||
|
21 | """ | |||
|
22 | Created on April 9, 2010 | |||
|
23 | Model for users | |||
|
24 | @author: marcink | |||
|
25 | """ | |||
|
26 | ||||
6 | from pylons_app.model.db import User |
|
27 | from pylons_app.model.db import User | |
7 | from pylons_app.model.meta import Session |
|
28 | from pylons_app.model.meta import Session | |
8 | ''' |
|
|||
9 | Created on Apr 9, 2010 |
|
|||
10 |
|
||||
11 | @author: marcink |
|
|||
12 | ''' |
|
|||
13 |
|
29 | |||
14 | class UserModel(object): |
|
30 | class UserModel(object): | |
15 |
|
31 | |||
16 | def __init__(self): |
|
32 | def __init__(self): | |
17 | self.sa = Session() |
|
33 | self.sa = Session() | |
18 |
|
34 | |||
19 | def get_user(self, id): |
|
35 | def get_user(self, id): | |
20 | return self.sa.query(User).get(id) |
|
36 | return self.sa.query(User).get(id) | |
21 |
|
37 | |||
22 | def create(self, form_data): |
|
38 | def create(self, form_data): | |
23 | try: |
|
39 | try: | |
24 | new_user = User() |
|
40 | new_user = User() | |
25 | for k, v in form_data.items(): |
|
41 | for k, v in form_data.items(): | |
26 | setattr(new_user, k, v) |
|
42 | setattr(new_user, k, v) | |
27 |
|
43 | |||
28 | self.sa.add(new_user) |
|
44 | self.sa.add(new_user) | |
29 | self.sa.commit() |
|
45 | self.sa.commit() | |
30 | except: |
|
46 | except: | |
31 | self.sa.rollback() |
|
47 | self.sa.rollback() | |
32 | raise |
|
48 | raise | |
33 |
|
49 | |||
34 | def update(self, id, form_data): |
|
50 | def update(self, id, form_data): | |
35 | try: |
|
51 | try: | |
36 | new_user = self.sa.query(User).get(id) |
|
52 | new_user = self.sa.query(User).get(id) | |
37 | for k, v in form_data.items(): |
|
53 | for k, v in form_data.items(): | |
38 | if k == 'new_password' and v != '': |
|
54 | if k == 'new_password' and v != '': | |
39 |
|
55 | |||
40 | new_user.password = v |
|
56 | new_user.password = v | |
41 | else: |
|
57 | else: | |
42 | setattr(new_user, k, v) |
|
58 | setattr(new_user, k, v) | |
43 |
|
59 | |||
44 | self.sa.add(new_user) |
|
60 | self.sa.add(new_user) | |
45 | self.sa.commit() |
|
61 | self.sa.commit() | |
46 | except: |
|
62 | except: | |
47 | self.sa.rollback() |
|
63 | self.sa.rollback() | |
48 | raise |
|
64 | raise |
@@ -1,21 +1,21 b'' | |||||
1 | ## -*- coding: utf-8 -*- |
|
1 | ## -*- coding: utf-8 -*- | |
2 | <%inherit file="/base/base.html"/> |
|
2 | <%inherit file="/base/base.html"/> | |
3 |
|
3 | |||
4 | <%def name="title()"> |
|
4 | <%def name="title()"> | |
5 | ${_('Permissions administration')} |
|
5 | ${_('Permissions administration')} | |
6 | </%def> |
|
6 | </%def> | |
7 | <%def name="breadcrumbs()"> |
|
7 | <%def name="breadcrumbs()"> | |
8 | ${h.link_to(u'Admin',h.url('admin_home'))} |
|
8 | ${h.link_to(u'Admin',h.url('admin_home'))} | |
9 | / |
|
9 | / | |
10 | ${_('Permissions')} |
|
10 | ${_('Permissions')} | |
11 | </%def> |
|
11 | </%def> | |
12 | <%def name="page_nav()"> |
|
12 | <%def name="page_nav()"> | |
13 | ${self.menu('admin')} |
|
13 | ${self.menu('admin')} | |
14 | ${self.submenu('permissions')} |
|
14 | ${self.submenu('permissions')} | |
15 | </%def> |
|
15 | </%def> | |
16 | <%def name="main()"> |
|
16 | <%def name="main()"> | |
17 | <div> |
|
17 | <div> | |
18 | <h2>${_('Permissions')}</h2> |
|
18 | <h2>${_('Permissions')}</h2> | |
19 | todo :) |
|
19 | ||
20 | </div> |
|
20 | </div> | |
21 | </%def> |
|
21 | </%def> |
General Comments 0
You need to be logged in to leave comments.
Login now