Compare commits

...

317 Commits

Author SHA1 Message Date
chenfeiCF
b7ba4d4a38 * fix bug: can not get queryID when browseType is 'bySearch'. 2016-03-15 11:55:15 +08:00
wangyidong
cf4e151f10 * add changelog. 2016-03-15 11:21:43 +08:00
wangyidong
bfee27a912 * fix bug #804. 2016-03-15 11:21:12 +08:00
wangyidong
b57e0ba64f * change for zh-tw lang. 2016-03-15 11:06:21 +08:00
wangyidong
17ac3a8996 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-15 11:00:34 +08:00
wangyidong
dcb944f0ac * change for priv. 2016-03-15 11:00:07 +08:00
chenfeiCF
9de63a3b15 * adjust view style for view method of task module. 2016-03-15 10:58:04 +08:00
wangyidong
9b339ac01f * fix bug get args for batchCheckIF. 2016-03-15 10:39:16 +08:00
wangyidong
0b3ef6fba1 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-15 09:57:51 +08:00
wangyidong
0e7c551027 * fix zget function and fix error. 2016-03-15 09:57:03 +08:00
wangyidong
7f2c61c891 * change for browse notice. 2016-03-15 09:56:33 +08:00
wangyidong
7a0c793270 * adjust framework and check mobile. 2016-03-15 09:53:16 +08:00
chenfeiCF
c6d21eec38 * add confirmBug action icon for zentao mobile web. 2016-03-15 09:51:05 +08:00
wangyidong
8c32fc916a Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-14 16:00:26 +08:00
Catouse
e549511cbb Merge branch 'master' of https://github.com/easysoft/zentaopms 2016-03-14 15:59:13 +08:00
wangyidong
b5a42dedda * fix beginIF for dao.class.php and saveQueryCondition for doc. 2016-03-14 15:59:12 +08:00
Catouse
c374a6e482 * fix table border in iframe modal in firefox. 2016-03-14 15:58:59 +08:00
wangyidong
e843e0004d * adjust code for ajaxGetSonModules. 2016-03-14 14:56:20 +08:00
wangyidong
d0a31b775e * change testcase getByAssigento => getByAssignedTo, getByOpened => getByOpenedBy. 2016-03-14 14:51:11 +08:00
wangyidong
fa11f088a6 * change getWillClose to get2BeClosed and adjust width for create story. 2016-03-14 14:44:28 +08:00
wangyidong
0bd5173f01 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-14 14:40:26 +08:00
wangyidong
14f376e399 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-14 14:39:35 +08:00
wangyidong
01fec363c4 * remove a css style. 2016-03-14 14:38:15 +08:00
wangyidong
9cfcab7dff Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-14 14:29:49 +08:00
wangyidong
96a8ff86f7 * adjust for check session. 2016-03-14 14:29:27 +08:00
wangyidong
82a46bd196 * fix bug for check icovn or mb_convert_encoding and check Directory separator. 2016-03-14 14:17:41 +08:00
wangyidong
ff43f65926 * adjust var name and adjust compressImage function. 2016-03-14 14:03:27 +08:00
wangyidong
5dfd9e57e9 * adjust var name and add notes for custom. 2016-03-14 13:25:45 +08:00
wangyidong
b5059fa0a5 * adjust code for var name. 2016-03-14 12:59:42 +08:00
wangyidong
81c7452008 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-14 12:55:31 +08:00
wangyidong
f3bb4d7e3e * fix for parseJSON. 2016-03-14 12:55:04 +08:00
chenfeiCF
b9b6821d36 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-14 12:00:32 +08:00
Catouse
d9ea62d417 Merge branch 'master' of https://github.com/easysoft/zentaopms 2016-03-14 17:15:50 +08:00
Catouse
d0030e82d3 * fix table border in firefox. 2016-03-14 17:15:42 +08:00
chenfeiCF
8bbad78ec1 * 1.added story verify in task view.
* 2.put story spec, verify and file together.
2016-03-14 11:59:34 +08:00
chenfeiCF
b2d49204b8 * move checkIP method to common model from user model. 2016-03-14 11:31:02 +08:00
wangyidong
0f6e89bd70 - rm treeview.html.php. 2016-03-14 11:28:01 +08:00
wangyidong
516b1f0034 * change hasEdited to editedByOther in common lang. 2016-03-14 10:48:42 +08:00
wangyidong
ae46e92be5 * change selectAll to selectButton in list. 2016-03-14 10:43:04 +08:00
wangyidong
3d349b45d8 * remove unset lastEditedDate. 2016-03-14 09:32:30 +08:00
chenfeiCF
cc24cd982c Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-14 09:11:15 +08:00
wangyidong
9b050fea16 * change mailtitle to mailTitle. 2016-03-14 09:08:06 +08:00
wangyidong
800a0efd9e * check unique add branch condition when create build and release. 2016-03-14 09:05:58 +08:00
chenfeiCF
70776effd6 * 1.change ajaxUnlink method to unlink in order to adding privilege.
* 2.add method lang item for managePriv in group module.
2016-03-14 09:00:57 +08:00
chenfeiCF
0d7558d206 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-14 09:00:12 +08:00
chenfeiCF
6c26ebb27f * add method privilege for link or unlink story, bug, case. 2016-03-14 08:55:38 +08:00
wangyidong
c604bf2ba2 * adjust for control send fail. 2016-03-14 08:39:16 +08:00
wangyidong
5188f6aa0b Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-14 08:10:15 +08:00
chenfeiCF
4f4ddb3e5f * if task is from bug, setImgSize for bug steps. 2016-03-14 06:09:14 +08:00
chenfeiCF
0bebfe176a * delete lang items which have not been used. 2016-03-14 02:44:40 +08:00
chenfeiCF
eb20cc4741 * fix the bug: not load search model when calling setSearchParams method. 2016-03-13 11:54:47 +08:00
chenfeiCF
0190934f07 * add empty mode for getPairs method of project model. 2016-03-13 10:56:16 +08:00
chenfeiCF
6b055c87fe * put getDeptUserPairs method from group model to dept model. 2016-03-13 08:02:35 +08:00
chenfeiCF
bf33b5f108 * adjust code for getUsers method of company module. 2016-03-13 07:49:46 +08:00
chenfeiCF
ab8f86396f * adjust buildSearchForm method: put setSearchParams method in model. 2016-03-13 07:43:03 +08:00
chenfeiCF
931a13641b * 1.added action labels for link or unlink action.
* 2.added desc of actions for link or unlink related case.
2016-03-13 05:19:13 +08:00
chenfeiCF
80a3959415 * 1.change deleteLinkedCase method name to unlinkCase.
* 2.adjust style for linkCases.html.php and delete lang item for link and unlink action.
2016-03-13 05:11:59 +08:00
chenfeiCF
4dcaa49d5b * 1.change deleteLinkedBug method name to unlinkBug.
* 2.adjust code for bug module.
2016-03-13 04:42:47 +08:00
chenfeiCF
a3c5e42a7c * 1.change deleteLinkedStory method name to unlinkStory.
* 2.adjust the code for story module.
2016-03-13 04:26:13 +08:00
chenfeiCF
eff03084c3 * if save search query successfully, display the query title in query select box. 2016-03-12 22:09:06 +08:00
chenfeiCF
4763dc65f7 * change the code style for browse method of release module. 2016-03-12 21:40:24 +08:00
chenfeiCF
55c2f7e63a * change the code style for story.js of project module. 2016-03-12 21:36:26 +08:00
chenfeiCF
91194fcccc * change getStoryStages method name to batchGetStoryStage. 2016-03-12 21:32:03 +08:00
chenfeiCF
bbbd0891ff * put the hint of preAndNext shortcut key on the back of title. 2016-03-12 21:16:34 +08:00
wangyidong
1a8a8a0be4 * adjust mail style. 2016-03-11 17:31:39 +08:00
wangyidong
aea3e80a3f * adjust style for bug create. 2016-03-11 15:19:00 +08:00
wangyidong
563541db13 * adjust code. 2016-03-11 15:01:09 +08:00
wangyidong
2a748ac35f * adjust required style. 2016-03-11 14:56:58 +08:00
wangyidong
470eb3a281 * adjust code for common. 2016-03-11 14:56:36 +08:00
wangyidong
2a8d4e92eb Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-11 14:51:54 +08:00
Catouse
a4ca492969 * set min height for form in iframe modal. 2016-03-11 13:06:54 +08:00
Catouse
7e626c0b6b * fix UI for modal trigger and ajax replace. 2016-03-11 13:04:02 +08:00
Catouse
cd760834af * ajust code. 2016-03-11 12:24:57 +08:00
Catouse
51b555c101 * change modal trigger markup. 2016-03-11 12:24:48 +08:00
Catouse
81c9b4d020 * prevent default behavior for modal trigger. 2016-03-11 11:42:57 +08:00
Catouse
41df2d107b * change shadow effect for main UI. 2016-03-11 11:42:32 +08:00
Catouse
80957347ad * change order of buttons in bootbox by update zui. 2016-03-11 10:07:34 +08:00
wangyidong
ba4efcfa7f Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-11 09:32:02 +08:00
wangyidong
b0e0241676 * change for framework. 2016-03-11 09:02:31 +08:00
chenfeiCF
7c349639df * 1.add linkCases method for testcase module.
* 2.hide fromBug or toBugs if not exist for testcase.
2016-03-11 07:30:36 +08:00
chenfeiCF
026dbe2f25 * add view file for linkBugs method. 2016-03-11 07:25:58 +08:00
chenfeiCF
6f9b3d3e61 * 1.add link bug method for bug module.
* 2.hide toStory, toTask or toCase when not exit for bug.
2016-03-11 07:13:47 +08:00
chenfeiCF
1e22ef4ea7 * adjust code for linkStory method and change the style for edit method. 2016-03-11 06:55:17 +08:00
Catouse
5f975e60ba * change style for table fixed in scrolling. 2016-03-10 18:23:00 +08:00
Catouse
4fc1f5286b * update zui. 2016-03-10 17:46:39 +08:00
wangyidong
142915aa30 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-10 11:16:00 +08:00
wangyidong
c96be8964a * change for framework. 2016-03-10 11:15:24 +08:00
wangyidong
2417ceeea2 * fix style. 2016-03-10 10:26:29 +08:00
wangyidong
019969ab90 * fix for fixed tfoot. 2016-03-10 10:25:59 +08:00
chenfeiCF
7ba54fb85b * add getBySearch method for testcase model.php. 2016-03-10 10:20:35 +08:00
wangyidong
0b99aaea7e * adjust for fix tfoot action. 2016-03-10 10:08:12 +08:00
wangyidong
b899f67e5c Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-10 09:32:11 +08:00
wangyidong
dffd808378 * change for lib can extend with base. 2016-03-10 09:30:15 +08:00
wangyidong
e617014b65 * adjust for fixed tfoot action. 2016-03-10 09:29:42 +08:00
chenfeiCF
6d6d0a85dd * change the return type for ajaxGetLinkedStories, ajaxDeleteLinkedStory, linkStories, deleteLinkedStory, getLinkedStories method. 2016-03-10 07:41:53 +08:00
chenfeiCF
c30be46e52 * 1.use ajax when delete search query.
* 2.adjust for task #2467.
2016-03-09 22:30:41 +08:00
chenfeiCF
e996a9d4ed * 1.change 'Set view' to 'Assign' for company and product module.
* 2.adjust code and fix error for linkstory method of story module.
2016-03-09 21:30:53 +08:00
wangyidong
61fb9e38ba Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-09 17:36:40 +08:00
wangyidong
2e7698db31 * change for framework. 2016-03-09 17:36:02 +08:00
wangyidong
4569039d98 * fix style for selectButton. 2016-03-09 17:35:26 +08:00
wangyidong
7da16e22ca * change index name for db. 2016-03-09 17:35:11 +08:00
chenfeiCF
140a15b0be * change the dao property name for task #2458. 2016-03-09 16:50:36 +08:00
chenfeiCF
7a1eb61efd Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-09 13:15:25 +08:00
chenfeiCF
40a300031e * adjust code for story module. 2016-03-09 13:14:36 +08:00
wangyidong
12bdd87463 * fix bug. 2016-03-09 13:13:50 +08:00
wangyidong
6f673a06ed Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-09 13:13:13 +08:00
chenfeiCF
80b9d18d73 * add link story method when edit a story. 2016-03-09 13:03:06 +08:00
wangyidong
81375e9147 * adjust code for fixed table. 2016-03-09 09:32:19 +08:00
wangyidong
a04db8bc49 * change for upgrade. 2016-03-08 16:27:47 +08:00
wangyidong
4f7b7266f4 * adjust code for custom lang. 2016-03-08 15:37:13 +08:00
wangyidong
3525fe15bc * fix error. 2016-03-08 13:50:09 +08:00
wangyidong
152ad148e6 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-08 11:34:12 +08:00
wangyidong
df85f2625e * finish task #2474 and fix bug. 2016-03-08 11:32:48 +08:00
Catouse
454d7d4942 * change style in default theme for datatable. 2016-03-08 09:15:43 +08:00
wangyidong
26d831d0b6 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-08 09:10:18 +08:00
Catouse
8c05dd59f4 * improve UI of forms for create action. 2016-03-07 14:07:49 +08:00
Catouse
18f97e6478 * change UI of buttons in table footer. 2016-03-07 13:35:13 +08:00
chenfeiCF
d97d22c0db * add story files and comments for task view. 2016-03-07 13:16:51 +08:00
wangyidong
5722d28e77 * fix bug #785. 2016-03-07 11:15:58 +08:00
wangyidong
fb65e3660b * finish task #2494. 2016-03-07 10:06:20 +08:00
Catouse
b3583677de * change style of priority icon. 2016-03-04 17:30:45 +08:00
Catouse
8c911f45da Merge branch 'master' of https://github.com/easysoft/zentaopms 2016-03-04 17:17:55 +08:00
Catouse
dad0b0395d * change UI for create forms. 2016-03-04 17:17:42 +08:00
wangyidong
4017b1ae75 * set time limit to 7200. 2016-03-04 15:21:18 +08:00
wangyidong
7603866e92 * merge in db and adjust code. 2016-03-04 14:50:48 +08:00
wangyidong
0d47f23a19 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-04 14:43:21 +08:00
chenfeiCF
3e19bde847 * merge the update8.0.1.sql. 2016-03-04 14:40:51 +08:00
wangyidong
f6186e949c * adjust code for dao check. 2016-03-04 14:15:01 +08:00
wangyidong
f36272ed99 * fix bug #772. 2016-03-04 14:14:15 +08:00
wangyidong
401e2c0ac1 * fix bug #789. 2016-03-04 13:50:01 +08:00
wangyidong
9cc1a9e055 * adjust code. 2016-03-04 13:36:33 +08:00
wangyidong
051e6530d8 * fix bug #795. 2016-03-04 13:36:09 +08:00
wangyidong
833d40aba6 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-04 13:08:31 +08:00
wangyidong
8bb0290077 * fix bug #801. 2016-03-04 13:07:20 +08:00
Catouse
51d75e71a2 * add method to init priority selectors. 2016-03-04 13:01:28 +08:00
Catouse
0dafbb66dd Merge branch 'master' of https://github.com/easysoft/zentaopms
Conflicts:
	www/js/my.full.js
2016-03-04 12:20:20 +08:00
Catouse
f1454d2d99 * create new UI for priority selectors. 2016-03-04 12:18:42 +08:00
Catouse
f4980f8377 * add css helpers. 2016-03-04 12:17:53 +08:00
Catouse
2fc659447f * add white space to button and link in login form. 2016-03-04 12:17:23 +08:00
Catouse
4f9fd70b9e * improve UI of story/create view. 2016-03-04 12:17:03 +08:00
wangyidong
59c99c29f3 * adjust code for bug #792. 2016-03-04 11:36:21 +08:00
wangyidong
d9e1606b2e * fix bug #792. 2016-03-04 11:33:54 +08:00
wangyidong
bc18f5d692 * fix bug #796. 2016-03-04 10:36:18 +08:00
wangyidong
a8089272bc Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-03 15:42:19 +08:00
wangyidong
59917d3a0a * finish task #2424. 2016-03-03 15:41:00 +08:00
chenfeiCF
2dbfa69498 * finish task #2471. 2016-03-03 13:58:22 +08:00
chenfeiCF
2f9ac5d436 * refactor task method for project module. 2016-03-02 17:35:05 +08:00
wangyidong
46eeec68aa Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-02 15:25:40 +08:00
wangyidong
274639baa5 * fix bug. 2016-03-02 15:25:27 +08:00
chenfeiCF
af9f3f8d37 * refactor browse method for doc module. 2016-03-02 14:24:49 +08:00
wangyidong
09c8fa8aa8 Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-02 13:28:44 +08:00
wangyidong
20aa49dea9 * fix bug #797 and adjust frawework. 2016-03-02 13:28:04 +08:00
chenfeiCF
abfb3f5fa5 * adjust code for browse method of release module. 2016-03-02 11:11:57 +08:00
chenfeiCF
a4fbf1ddef * refactor browse method for testcase module. 2016-03-02 10:46:28 +08:00
chenfeiCF
12ec619856 * refactor browse method for product module. 2016-03-01 08:28:35 +08:00
chenfeiCF
c7168f7db7 * refactor browse method for company module. 2016-03-01 06:52:21 +08:00
chenfeiCF
cb7663e691 * adjust code for bug module. 2016-03-01 06:21:48 +08:00
wangyidong
c1a45df1cd Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-01 14:48:20 +08:00
wangyidong
ffd58aebb7 + add myrouter class. 2016-03-01 14:48:02 +08:00
wangyidong
da0b5e611d Merge branch 'master' of github.com:easysoft/zentaopms 2016-03-01 10:58:35 +08:00
wangyidong
e23311969d * change for new framework. 2016-03-01 10:57:53 +08:00
chenfeiCF
127d2e6a40 * refactor browse method for bug module. 2016-03-01 06:00:25 +08:00
chenfeiCF
7f66f2018e * finish task #2501. 2016-03-01 02:09:18 +08:00
chenfeiCF
8a604925ee * finish task #2492. 2016-02-29 10:16:34 +08:00
chenfeiCF
288bfe5758 * finish task *2467. 2016-02-25 15:21:04 +08:00
chenfeiCF
3abf28e9ed * adjust style of file/buildform. 2016-02-25 15:12:11 +08:00
QingDao Nature Easy Soft Network Technology Co,LTD
69f7a3341e Merge pull request #3 from syncxplus/master
* Update chmod command in Makefile, working on Mac OSX
2016-02-24 17:05:54 +08:00
wangyidong
7bb16d8f92 * finish task #2510. 2016-02-24 16:00:21 +08:00
jibo
f3edc332fd Update chmod command in Makefile, working on Mac OSX 2016-02-24 15:40:43 +08:00
wangyidong
b321098c26 * finish task #2508. 2016-02-24 14:52:54 +08:00
wangyidong
8c3028d3c0 Merge branch 'master' of github.com:easysoft/zentaopms 2016-02-24 13:58:30 +08:00
wangyidong
997f691665 * code for task #2470. 2016-02-24 13:06:59 +08:00
Catouse
8a5df03463 * fix style of group/managemember. 2016-02-24 10:34:58 +08:00
wangyidong
a261867468 Merge branch 'master' of github.com:easysoft/zentaopms 2016-02-24 08:41:08 +08:00
wangyidong
b8474bddf5 * fix bug. 2016-02-24 08:38:49 +08:00
chenfeiCF
7683db7228 * finish task #2459. 2016-02-23 21:46:26 +08:00
chenfeiCF
0269715534 * adjust code for task #2015. 2016-02-23 21:03:20 +08:00
chenfeiCF
8499ea4819 * finish task #2015. 2016-02-23 20:56:35 +08:00
Catouse
8373cc09cf + add zentao logo. 2016-02-23 16:56:02 +08:00
Catouse
334a0b1009 * change UI of mail content for user. 2016-02-23 16:55:50 +08:00
Catouse
ae4fd31b3d Merge branch 'master' of https://github.com/easysoft/zentaopms 2016-02-23 15:43:37 +08:00
Catouse
ea56bf4d25 * ajust action history list style. 2016-02-23 14:36:02 +08:00
chenfeiCF
98e7f59798 * finish task #2482. 2016-02-23 13:41:52 +08:00
chenfeiCF
baa2b472c2 * finish task #2493. 2016-02-22 21:49:51 +08:00
Catouse
8c284f4459 * change alert style in mail/save UI. 2016-02-22 17:21:17 +08:00
Catouse
2bde4bcba2 * disable autocomplete of date input. 2016-02-22 17:21:04 +08:00
Catouse
2f8121190d * finish task #2509. 2016-02-22 17:08:45 +08:00
Catouse
338cfa239b * remove right border of button in search form. 2016-02-22 15:32:50 +08:00
Catouse
d06f0ecb5f * change margin of titlebar. 2016-02-22 15:29:39 +08:00
Catouse
80b50cb041 * ajust style of project/manageproducts UI. 2016-02-22 15:14:31 +08:00
Catouse
9141bb13d7 * fix style of tree menu. 2016-02-22 15:03:44 +08:00
Catouse
0f2f5eaf1a * change admin/sso UI. 2016-02-22 13:46:54 +08:00
Catouse
f3ab28ebdd * update zui. 2016-02-22 13:46:18 +08:00
Catouse
173fa51902 * fix chosen style. 2016-02-22 13:39:53 +08:00
Catouse
e35b0f273e * fix style of inline radios. 2016-02-22 13:39:43 +08:00
Catouse
44d380dd34 * update zui to 1.4. 2016-02-22 13:39:17 +08:00
Catouse
4b76d36e9e * finish task #2469. 2016-02-22 10:29:28 +08:00
wangyidong
1fce9b6d68 Merge branch 'master' of github.com:easysoft/zentaopms 2016-02-19 14:35:30 +08:00
wangyidong
bbf3d672af * code for task #2464. 2016-02-19 14:35:03 +08:00
chenfeiCF
d9ddbd0450 * finish task #2458. 2016-02-19 13:49:25 +08:00
wangyidong
aa6f3bc46d Merge branch 'master' of github.com:easysoft/zentaopms 2016-02-19 11:06:16 +08:00
wangyidong
33d94add3b * code for task #2460. 2016-02-19 11:04:53 +08:00
chenfeiCF
46136fbf70 * adjust for task #2478. 2016-02-19 09:46:53 +08:00
wangyidong
95de1da365 Merge branch 'master' of github.com:easysoft/zentaopms 2016-02-19 08:59:57 +08:00
chenfeiCF
72c1f7dc8a Merge branch 'master' of github.com:easysoft/zentaopms 2016-02-18 22:27:01 +08:00
chenfeiCF
9f99387a7e * finish task #2457. 2016-02-18 21:38:54 +08:00
wangyidong
77b0403e53 * adjust code for getProjectBugs. 2016-02-18 17:26:44 +08:00
wangyidong
19044c3115 Merge branch 'master' of github.com:easysoft/zentaopms 2016-02-18 17:19:08 +08:00
wangyidong
a24f6cbeba * finish task #2466. 2016-02-18 17:18:20 +08:00
chenfeiCF
8517690be4 * finish task #2478. 2016-02-18 12:16:43 +08:00
wangyidong
ae81e0f85e * adjust code for upload image. 2016-02-18 11:00:05 +08:00
wangyidong
83d41f0345 * finish task #2502. 2016-02-18 10:59:37 +08:00
wangyidong
39928a235f * adjust code for testtask. 2016-02-18 09:14:37 +08:00
wangyidong
454ed3acb2 * adjust code for task #2465. 2016-02-17 16:31:30 +08:00
wangyidong
ff800c6133 Merge branch 'master' of github.com:easysoft/zentaopms 2016-02-17 16:22:49 +08:00
wangyidong
8230ffd6d0 * finish task #2465. 2016-02-17 16:22:24 +08:00
chenfeiCF
ae9b46a05f * finish task #2495. 2016-02-17 15:20:51 +08:00
wangyidong
d01b8eb8df * finish task #2489. 2016-02-17 14:18:54 +08:00
wangyidong
8b1c8fb46f * code for task #1799. 2016-02-17 10:40:17 +08:00
wangyidong
26e611bee5 * finish task #1799. 2016-02-17 10:16:18 +08:00
wangyidong
46a7ceaad7 Merge branch 'master' of github.com:easysoft/zentaopms 2016-02-17 09:09:00 +08:00
wangyidong
30b9c0d99b * finish task #2498. 2016-02-17 09:07:46 +08:00
chenfeiCF
63b4164434 * finish task *2505. 2016-02-16 17:27:23 +08:00
wangyidong
2f9807ab14 Merge branch 'master' of github.com:easysoft/zentaopms 2016-02-16 14:56:57 +08:00
wangyidong
50b3d69bc9 * finish task #2462. 2016-02-16 14:56:08 +08:00
chenfeiCF
fcc798f4d4 * finish task #2507. 2016-02-16 14:05:45 +08:00
wangyidong
d324bb72e6 Merge branch 'master' of github.com:easysoft/zentaopms 2016-02-15 17:29:46 +08:00
wangyidong
e8bb9aa1ee * finish task #1295. 2016-02-15 17:29:23 +08:00
chenfeiCF
5266c8c064 * finish task #2499. 2016-02-15 15:42:21 +08:00
chenfeiCF
86ee7a2055 * finish task #2503. 2016-02-15 15:21:29 +08:00
chenfeiCF
ef9f7ca2fb * finish task #2506. 2016-02-15 14:39:31 +08:00
wangyidong
1f9055b610 * fix bugs and adjust code. 2016-01-25 13:50:58 +08:00
wangyidong
2b7df545f2 Merge branch 'master' of github.com:easysoft/zentaopms 2016-01-11 11:33:13 +08:00
wangyidong
ba3ed90986 * fix bug for report getUserTesttasks. 2016-01-11 11:32:51 +08:00
Catouse
6acdab6742 * fix kindeditor crash in ie8. 2016-01-08 09:25:17 +08:00
wangyidong
a0d475720b * fix error. 2016-01-05 15:22:04 +08:00
wangyidong
052a1d7a7c * fix bug #783. 2016-01-05 15:21:25 +08:00
wangyidong
a2e1a381e2 * fix bug for backup db. 2016-01-04 09:43:24 +08:00
wangyidong
e3c51a6200 * adjust for merge model file. 2015-12-31 15:54:27 +08:00
chenfeiCF
ce148bb582 Merge branch 'master' of github.com:easysoft/zentaopms 2015-12-30 16:08:04 +08:00
chenfeiCF
dfa64599e2 * fix a bug for action module. 2015-12-30 15:55:33 +08:00
wangyidong
ca7e1ab6f7 * fix bug #750. 2015-12-30 14:41:11 +08:00
wangyidong
0f4856495a * fix bug #781. 2015-12-24 15:07:20 +08:00
wangyidong
888c89fbe5 * change for upgrade. 2015-12-24 09:59:08 +08:00
wangyidong
731bc2e5a8 * fix bug #776. 2015-12-24 09:48:03 +08:00
wangyidong
fb2357ef32 * fix bug for create bug in branch. 2015-12-23 17:23:30 +08:00
wangyidong
82f04a6a22 * fix bug #774. 2015-12-23 16:33:47 +08:00
wangyidong
4ebfcc3ac8 * fix bug #775. 2015-12-23 13:45:19 +08:00
wangyidong
e45a196b7b * fix bugs. 2015-12-23 09:29:38 +08:00
wangyidong
957111c6f0 * code for bug #777. 2015-12-22 16:56:55 +08:00
wangyidong
1e3c913063 * fix bug #777. 2015-12-22 16:55:25 +08:00
wangyidong
b6e4fc2e41 * fix bug #778. 2015-12-22 15:10:55 +08:00
wangyidong
b49fae3d74 * fix bug #779. 2015-12-22 15:01:21 +08:00
wangyidong
7007f4b888 * finish task #2425. 2015-12-22 13:27:26 +08:00
wangyidong
aab0b66e15 * finish task #2426. 2015-12-21 17:27:57 +08:00
wangyidong
9c6d301c9c * finish task #2428. 2015-12-21 16:02:41 +08:00
wangyidong
62c590a94b * finish task #2431. 2015-12-21 14:20:13 +08:00
chenfeiCF
b5d020c652 * fix a bug. 2015-12-16 13:49:09 +08:00
wangyidong
72eca957bf * fix bug for project link story when branch is empty. 2015-12-10 13:50:05 +08:00
wangyidong
92208eae36 * fix error for HTMLPurifier. 2015-12-04 13:07:50 +08:00
wangyidong
b3f919b697 * fix bug for update user. 2015-12-03 15:49:31 +08:00
wangyidong
6ae74c1430 * fix for zdb class. 2015-12-02 17:32:51 +08:00
wangyidong
33dff40092 * fix bug for get build pairs. 2015-12-02 16:21:31 +08:00
wangyidong
8fccef5df3 * fix bug #773. 2015-12-01 15:31:24 +08:00
wangyidong
5933daa169 * change js parents to closest. 2015-12-01 11:03:29 +08:00
wangyidong
a2b2454760 * fix a bug for branch. 2015-12-01 11:03:08 +08:00
wangyidong
b808b0c8c3 * change for build and release view page. 2015-11-30 14:23:36 +08:00
wangyidong
0160e99054 * adjust style. 2015-11-30 14:19:25 +08:00
wangyidong
036cebc87e * fix js error. 2015-11-27 15:46:22 +08:00
daitingting
290d1a76bc * Adjust codes. 2015-11-27 15:40:23 +08:00
daitingting
61ecdb6000 * Add more link for blocks. 2015-11-27 15:26:19 +08:00
chujilu
5913f76f35 Merge branch 'master' of https://github.com/easysoft/zentaopms 2015-11-27 14:49:31 +08:00
chujilu
d04a6a0ba8 * fix block user account 2015-11-27 14:48:58 +08:00
wangyidong
7acbf8c7bd * disabled for ie8/9 placeholder. 2015-11-27 14:06:59 +08:00
wangyidong
9fe3b5ca05 Merge branch 'master' of github.com:easysoft/zentaopms 2015-11-27 13:33:23 +08:00
Catouse
119f3bfb20 * fix placeholder for ie. 2015-11-27 13:30:02 +08:00
wangyidong
998f465811 * adjust story needReview. 2015-11-27 13:18:45 +08:00
Catouse
4bf02d062f * change admin/index style. 2015-11-27 12:05:02 +08:00
wangyidong
62fc4ac385 * change for zh-tw lang. 2015-11-27 09:21:17 +08:00
wangyidong
f020e482c6 * change for upgrade. 2015-11-26 17:33:03 +08:00
wangyidong
6d2f5f4c8e * delete sended mail. 2015-11-26 17:26:40 +08:00
wangyidong
4040b541dd * adjust for misc-about. 2015-11-26 16:18:03 +08:00
wangyidong
13c881ccda * fix bug #770. 2015-11-26 16:17:42 +08:00
wangyidong
a9f3fe5270 * fix bug #769. 2015-11-26 14:01:32 +08:00
wangyidong
02ce779b65 * adjust code. 2015-11-26 13:29:29 +08:00
wangyidong
48ada5974b * finish task #2416. 2015-11-26 13:11:01 +08:00
wangyidong
6670b94caf * adjust for user getById. 2015-11-26 09:20:53 +08:00
wangyidong
a94cd23f69 * finish task #2411. 2015-11-25 16:23:47 +08:00
wangyidong
514376c307 * finish task #2414. 2015-11-25 15:31:54 +08:00
wangyidong
aa8c0363f3 * finish task #2415. 2015-11-25 14:22:24 +08:00
wangyidong
849ec91753 * code for task #2417. 2015-11-25 13:15:54 +08:00
wangyidong
9012728c5a * change for dept. 2015-11-25 12:13:49 +08:00
wangyidong
3c4c0e137e * finish task #2417. 2015-11-25 10:11:02 +08:00
wangyidong
2aba28b253 * finish task #2418. 2015-11-24 12:07:39 +08:00
wangyidong
715050df57 * adjust for user. 2015-11-24 11:25:10 +08:00
wangyidong
8ff1e21c95 * change for ranzhi. 2015-11-24 11:15:00 +08:00
wangyidong
a01925a0f2 * finish task #2419. 2015-11-24 10:18:48 +08:00
wangyidong
6f67d9af0f * fix bug. 2015-11-24 09:31:35 +08:00
wangyidong
c3a9804a56 * finish task #2420. 2015-11-24 09:12:27 +08:00
wangyidong
5923c8e637 * finish task #2421. 2015-11-24 08:44:17 +08:00
wangyidong
eebc5834ae * fix for testcase show import. 2015-11-23 17:15:32 +08:00
wangyidong
75bd02ec48 * adjust width for english. 2015-11-23 16:22:51 +08:00
wangyidong
103919866f * fix bug #755. 2015-11-23 15:18:42 +08:00
wangyidong
57ef7bad74 * fix bug #768. 2015-11-23 14:25:58 +08:00
wangyidong
3d46d2e1ed * fix bug #663 in backyard pms. 2015-11-20 11:36:46 +08:00
wangyidong
c657433fa2 * adjust code and fix bug. 2015-11-20 10:45:18 +08:00
wangyidong
7374c81949 * fix bug #669 in backyard pms. 2015-11-20 10:06:59 +08:00
wangyidong
d06306de00 * fix bug #666 in backyard pms. 2015-11-20 10:05:42 +08:00
wangyidong
ba0f4306c6 * fix filter params. 2015-11-20 10:05:17 +08:00
wangyidong
35cd899cd5 * fix bug for get noterminate build. 2015-11-19 11:05:24 +08:00
wangyidong
a05357d964 * fix bug #656 in backyard pms. 2015-11-19 09:27:53 +08:00
wangyidong
7955810546 Merge branch 'master' of github.com:easysoft/zentaopms 2015-11-17 10:54:16 +08:00
wangyidong
6fe166a8f5 * fix bug for sql. 2015-11-17 10:53:58 +08:00
chenfeiCF
c1da3f39c2 * fix bug #766. 2015-11-16 13:39:05 +08:00
wangyidong
e17126e526 * add changelog. 2015-11-13 15:34:23 +08:00
daitingting
7c5358ed0d Merge branch 'master' of https://github.com/easysoft/zentaopms 2015-11-13 15:17:23 +08:00
daitingting
7184cb3a90 * Adjust codes. 2015-11-13 15:14:42 +08:00
403 changed files with 15697 additions and 10721 deletions

View File

@@ -38,9 +38,9 @@ pms:
# notify.zip.
mkdir zentaopms/www/data/notify/
# change mode.
chmod 777 -R zentaopms/tmp/
chmod 777 -R zentaopms/www/data
chmod 777 -R zentaopms/config
chmod -R 777 zentaopms/tmp/
chmod -R 777 zentaopms/www/data
chmod -R 777 zentaopms/config
chmod 777 zentaopms/module
chmod 777 zentaopms/www
chmod a+rx zentaopms/bin/*

View File

@@ -1 +1 @@
7.4.beta
8.1.stable

View File

@@ -6,19 +6,11 @@ error_reporting(E_ALL ^ E_NOTICE);
$pmsRoot = dirname(dirname(dirname(__FILE__)));
$myConfig = $pmsRoot . '/config/config.php';
$zipClass = $pmsRoot . '/lib/pclzip/pclzip.class.php';
$zdbClass = $pmsRoot . '/lib/zdb/zdb.class.php';
include $myConfig;
include $zipClass;
/* Judge mysqldump cmd setted or not. */
if(empty($config->mysqldump))
{
echo "Please set the mysqldump path in $myConfig:\n";
echo "Just like: \n";
echo '$config->mysqldump = \'/usr/bin/mysqldump\'; for linux' . "\n";
echo '$config->mysqldump = \'D:\xampp\mysql\bin\mysqldump.exe\'; for windows' . "\n";
exit;
}
include $zdbClass;
/* Init the backupRoot and dest directory. */
$backupRoot = $pmsRoot . "/backup";
@@ -29,12 +21,13 @@ if(!file_exists($destDir)) mkdir($destDir, 0777);
/* Backup database. */
$dbRawFile = "db." . date('Ymd') . ".sql";
$password = $config->db->password ? "-p{$config->db->password}" : ' ';
$command = "\"{$config->mysqldump}\" -u{$config->db->user} $password -P {$config->db->port} {$config->db->name} > {$dbRawFile}";
$dsn = "mysql:host={$config->db->host}; port={$config->db->port}; dbname={$config->db->name}";
$dbh = new PDO($dsn, $config->db->user, $config->db->password, array(PDO::ATTR_PERSISTENT => $config->db->persistant));
$dbh->exec("SET NAMES {$config->db->encoding}");
echo "Backuping database,";
system($command, $return);
if($return == 0)
$zdb = new zdb();
$return = $zdb->dump($dbRawFile);
if($return->result)
{
$dbZipFile = $destDir . "/" . str_replace("sql", "zip", $dbRawFile);
$archive = new pclzip($dbZipFile);

View File

@@ -17,7 +17,7 @@ if(!function_exists('getWebRoot')){function getWebRoot(){}}
/* Basic settings. */
$config = new config();
$config->version = '7.4.beta'; // The version of zentaopms. Don't change it.
$config->version = '8.1'; // The version of zentaopms. Don't change it.
$config->charset = 'UTF-8'; // The charset of zentaopms.
$config->cookieLife = time() + 2592000; // The cookie life time.
$config->timezone = 'Asia/Shanghai'; // The time zone setting, for more see http://www.php.net/manual/en/timezones.php
@@ -81,8 +81,11 @@ $config->file = new stdclass();
$config->file->dangers = 'php,php3,php4,phtml,php5,jsp,py,rb,asp,asa,cer,cdx,aspl'; // Dangerous files.
$config->file->maxSize = 1024 * 1024; // Max size.
/* IP white list settings.*/
$config->ipWhiteList = '*';
/* View type settings. */
$config->viewPrefix['mhtml'] = 'm.';
$config->devicePrefix['mhtml'] = 'm.';
/* Master database settings. */
$config->db = new stdclass();
@@ -101,6 +104,14 @@ $config->slaveDB->encoding = 'UTF8';
$config->slaveDB->strictMode = false;
$config->slaveDB->checkCentOS= true;
/* Framework config. */
$config->framework = new stdclass();
$config->framework->jsWithPrefix = false;
$config->framework->autoRepairTable = true;
$config->framework->logDays = 14;
$config->framework->purifier = true;
$config->framework->autoLang = false;
/* Include the custom config file. */
$configRoot = dirname(__FILE__) . DIRECTORY_SEPARATOR;
$myConfig = $configRoot . 'my.php';

View File

@@ -25,7 +25,7 @@ CREATE TABLE IF NOT EXISTS `zt_storystage` (
`stage` varchar(50) NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
ALTER TABLE `zt_story` ADD INDEX `product` (`product`, `module`, `type`, `pri`), DROP INDEX `product`;
ALTER TABLE `zt_story` ADD `plan` text COLLATE 'utf8_general_ci' NOT NULL AFTER `module`;
ALTER TABLE `zt_story` CHANGE `plan` `plan` text COLLATE 'utf8_general_ci' NOT NULL AFTER `module`;
UPDATE `zt_story` SET `plan`='' WHERE `plan`='0';
ALTER TABLE `zt_release` DROP INDEX `name`;

2
db/update7.4.beta.sql Normal file
View File

@@ -0,0 +1,2 @@
ALTER TABLE `zt_story` CHANGE `plan` `plan` text COLLATE 'utf8_general_ci' NOT NULL AFTER `module`;
UPDATE `zt_story` SET `plan`='' WHERE `plan`='0';

26
db/update8.0.1.sql Normal file
View File

@@ -0,0 +1,26 @@
ALTER TABLE `zt_action` ADD INDEX `action` (`objectID`, `product`, `project`, `action`, `date`);
ALTER TABLE `zt_branch` ADD INDEX `product` (`product`);
ALTER TABLE `zt_bug` ADD INDEX `bug` (`product`,`module`,`project`,`assignedTo`);
ALTER TABLE `zt_build` ADD INDEX `build` (`product`, `project`);
ALTER TABLE `zt_case` ADD INDEX `case` (`product`, `module`, `story`);
ALTER TABLE `zt_cron` ADD INDEX `lastTime` (`lastTime`);
ALTER TABLE `zt_dept` ADD INDEX `dept` (`parent`, `path`);
ALTER TABLE `zt_doc` ADD INDEX `doc` (`product`, `project`);
ALTER TABLE `zt_extension` ADD INDEX `extension` (`name`, `installedTime`), DROP INDEX `name`, DROP INDEX `addedTime`;
ALTER TABLE `zt_file` ADD INDEX `file` (`objectType`, `objectID`), DROP INDEX `objectType`, DROP INDEX `objectID`;
ALTER TABLE `zt_mailqueue` ADD INDEX `sendTime` (`sendTime`);
ALTER TABLE `zt_module` ADD INDEX `module` (`root`, `type`, `path`), DROP INDEX `root`, DROP INDEX `type`;
ALTER TABLE `zt_product` ADD INDEX `order` (`order`);
ALTER TABLE `zt_productplan` ADD INDEX `plan` (`product`, `end`);
ALTER TABLE `zt_project` ADD INDEX `project` (`parent`, `begin`, `end`, `status`, `order`), DROP INDEX `project`;
ALTER TABLE `zt_release` ADD INDEX `build` (`build`);
ALTER TABLE `zt_story` ADD INDEX `story` (`product`, `module`, `status`, `assignedTo`), DROP INDEX `status`, DROP INDEX `product`;
ALTER TABLE `zt_storystage` ADD INDEX `story` (`story`);
ALTER TABLE `zt_task` ADD INDEX `task` (`project`, `module`, `story`, `assignedTo`), DROP INDEX `project`, DROP INDEX `type`, DROP INDEX `status`;
ALTER TABLE `zt_testresult` ADD INDEX `testresult` (`case`, `version`, `run`), DROP INDEX `run`, DROP INDEX `case`;
ALTER TABLE `zt_testtask` ADD INDEX `build` (`build`);
ALTER TABLE `zt_todo` ADD INDEX `todo` (`account`, `date`), DROP INDEX `user`;
ALTER TABLE `zt_user` ADD INDEX `user` (`dept`, `email`, `commiter`), DROP INDEX `dept`;
ALTER TABLE `zt_usercontact` ADD INDEX `account` (`account`);
ALTER TABLE `zt_userquery` ADD INDEX `query` (`account`, `module`), DROP INDEX `account`, DROP INDEX `module`;
ALTER TABLE `zt_testtask` ADD `mailto` varchar(255) COLLATE 'utf8_general_ci' NOT NULL DEFAULT '' AFTER `end`;

View File

@@ -11,7 +11,8 @@ CREATE TABLE IF NOT EXISTS `zt_action` (
`comment` text NOT NULL,
`extra` text NOT NULL,
`read` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `action` (`objectID`,`product`,`project`,`action`,`date`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_branch`;
@@ -20,7 +21,8 @@ CREATE TABLE IF NOT EXISTS `zt_branch` (
`product` mediumint(8) unsigned NOT NULL,
`name` varchar(255) NOT NULL,
`deleted` enum('0','1') NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `product` (`product`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_bug`;
CREATE TABLE IF NOT EXISTS `zt_bug` (
@@ -69,7 +71,8 @@ CREATE TABLE IF NOT EXISTS `zt_bug` (
`lastEditedBy` varchar(30) NOT NULL default '',
`lastEditedDate` datetime NOT NULL,
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `bug` (`product`,`module`,`project`,`assignedTo`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_build`;
CREATE TABLE IF NOT EXISTS `zt_build` (
@@ -86,7 +89,8 @@ CREATE TABLE IF NOT EXISTS `zt_build` (
`builder` char(30) NOT NULL default '',
`desc` text NOT NULL,
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `build` (`product`,`project`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_burn`;
CREATE TABLE IF NOT EXISTS `zt_burn` (
@@ -130,7 +134,8 @@ CREATE TABLE IF NOT EXISTS `zt_case` (
`lastRunner` varchar(30) NOT NULL,
`lastRunDate` datetime NOT NULL,
`lastRunResult` char(30) NOT NULL,
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `case` (`product`,`module`,`story`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_casestep`;
CREATE TABLE IF NOT EXISTS `zt_casestep` (
@@ -182,7 +187,8 @@ CREATE TABLE IF NOT EXISTS `zt_cron` (
`buildin` tinyint(1) NOT NULL DEFAULT '0',
`status` varchar(20) NOT NULL,
`lastTime` datetime NOT NULL,
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `lastTime` (`lastTime`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_lang`;
CREATE TABLE IF NOT EXISTS `zt_lang` (
@@ -207,7 +213,8 @@ CREATE TABLE IF NOT EXISTS `zt_dept` (
`position` char(30) NOT NULL default '',
`function` char(255) NOT NULL default '',
`manager` char(30) NOT NULL default '',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `dept` (`parent`,`path`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_doc`;
CREATE TABLE IF NOT EXISTS `zt_doc` (
@@ -228,7 +235,8 @@ CREATE TABLE IF NOT EXISTS `zt_doc` (
`editedBy` varchar(30) NOT NULL,
`editedDate` datetime NOT NULL,
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `doc` (`product`,`project`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_doclib`;
CREATE TABLE IF NOT EXISTS `zt_doclib` (
@@ -272,8 +280,7 @@ CREATE TABLE IF NOT EXISTS `zt_extension` (
`status` varchar(20) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `code` (`code`),
KEY `name` (`name`),
KEY `addedTime` (`installedTime`)
KEY `extension` (`name`,`installedTime`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_file`;
CREATE TABLE IF NOT EXISTS `zt_file` (
@@ -289,7 +296,8 @@ CREATE TABLE IF NOT EXISTS `zt_file` (
`downloads` mediumint(8) unsigned NOT NULL default '0',
`extra` varchar(255) NOT NULL,
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `file` (`objectType`,`objectID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_group`;
CREATE TABLE IF NOT EXISTS `zt_group` (
@@ -315,7 +323,8 @@ CREATE TABLE IF NOT EXISTS `zt_history` (
`old` text NOT NULL,
`new` text NOT NULL,
`diff` mediumtext NOT NULL,
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `action` (`action`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_mailqueue`;
CREATE TABLE IF NOT EXISTS `zt_mailqueue` (
@@ -329,7 +338,8 @@ CREATE TABLE IF NOT EXISTS `zt_mailqueue` (
`sendTime` datetime NOT NULL,
`status` varchar(10) NOT NULL DEFAULT 'wait',
`failReason` text NOT NULL,
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `sendTime` (`sendTime`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_module`;
CREATE TABLE IF NOT EXISTS `zt_module` (
@@ -343,7 +353,8 @@ CREATE TABLE IF NOT EXISTS `zt_module` (
`order` smallint(5) unsigned NOT NULL default '0',
`type` char(30) NOT NULL,
`owner` varchar(30) NOT NULL,
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `module` (`root`,`type`,`path`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_product`;
CREATE TABLE IF NOT EXISTS `zt_product` (
@@ -363,7 +374,8 @@ CREATE TABLE IF NOT EXISTS `zt_product` (
`createdVersion` varchar(20) NOT NULL,
`order` mediumint(8) unsigned NOT NULL,
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `order` (`order`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_productplan`;
CREATE TABLE IF NOT EXISTS `zt_productplan` (
@@ -375,7 +387,8 @@ CREATE TABLE IF NOT EXISTS `zt_productplan` (
`begin` date NOT NULL,
`end` date NOT NULL,
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `plan` (`product`,`end`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_project`;
CREATE TABLE IF NOT EXISTS `zt_project` (
@@ -410,7 +423,7 @@ CREATE TABLE IF NOT EXISTS `zt_project` (
`order` mediumint(8) unsigned NOT NULL,
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `project` (`type`,`parent`,`begin`,`end`,`status`,`statge`,`pri`)
KEY `project` (`parent`,`begin`,`end`,`status`,`order`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_projectproduct`;
CREATE TABLE IF NOT EXISTS `zt_projectproduct` (
@@ -441,7 +454,8 @@ CREATE TABLE IF NOT EXISTS `zt_release` (
`desc` text NOT NULL,
`status` varchar(20) NOT NULL default 'normal',
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`),
PRIMARY KEY (`id`),
KEY `build` (`build`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_story`;
CREATE TABLE IF NOT EXISTS `zt_story` (
@@ -478,8 +492,7 @@ CREATE TABLE IF NOT EXISTS `zt_story` (
`version` smallint(6) NOT NULL default '1',
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `product` (`product`,`module`,`type`,`pri`),
KEY `status` (`status`)
KEY `story` (`product`,`module`,`status`,`assignedTo`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_storyspec`;
CREATE TABLE IF NOT EXISTS `zt_storyspec` (
@@ -494,7 +507,8 @@ CREATE TABLE IF NOT EXISTS `zt_storyspec` (
CREATE TABLE IF NOT EXISTS `zt_storystage` (
`story` mediumint(8) unsigned NOT NULL,
`branch` mediumint(8) unsigned NOT NULL,
`stage` varchar(50) NOT NULL
`stage` varchar(50) NOT NULL,
KEY `story` (`story`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_task`;
CREATE TABLE IF NOT EXISTS `zt_task` (
@@ -530,8 +544,8 @@ CREATE TABLE IF NOT EXISTS `zt_task` (
`lastEditedBy` varchar(30) NOT NULL,
`lastEditedDate` datetime NOT NULL,
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `type` (`type`)
PRIMARY KEY (`id`),
KEY `task` (`project`,`module`,`story`,`assignedTo`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_taskestimate`;
CREATE TABLE IF NOT EXISTS `zt_taskestimate` (
@@ -566,8 +580,7 @@ CREATE TABLE IF NOT EXISTS `zt_testresult` (
`lastRunner` varchar(30) NOT NULL,
`date` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `run` (`run`),
KEY `case` (`case`,`version`)
KEY `testresult` (`case`,`version`, `run`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_testrun`;
CREATE TABLE IF NOT EXISTS `zt_testrun` (
@@ -594,11 +607,13 @@ CREATE TABLE IF NOT EXISTS `zt_testtask` (
`pri` tinyint(3) unsigned NOT NULL default '0',
`begin` date NOT NULL,
`end` date NOT NULL,
`mailto` varchar(255) NOT NULL default '',
`desc` text NOT NULL,
`report` text NOT NULL,
`status` enum('blocked','doing','wait','done') NOT NULL DEFAULT 'wait',
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `build` (`build`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_todo`;
CREATE TABLE IF NOT EXISTS `zt_todo` (
@@ -615,7 +630,7 @@ CREATE TABLE IF NOT EXISTS `zt_todo` (
`status` enum('wait','doing','done') NOT NULL DEFAULT 'wait',
`private` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
KEY `user` (`account`)
KEY `todo` (`account`,`date`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_user`;
CREATE TABLE IF NOT EXISTS `zt_user` (
@@ -650,7 +665,7 @@ CREATE TABLE IF NOT EXISTS `zt_user` (
`deleted` enum('0','1') NOT NULL default '0',
PRIMARY KEY (`id`),
UNIQUE KEY `account` (`account`),
KEY `dept` (`dept`)
KEY `user` (`dept`,`email`,`commiter`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_usercontact`;
CREATE TABLE IF NOT EXISTS `zt_usercontact` (
@@ -658,7 +673,8 @@ CREATE TABLE IF NOT EXISTS `zt_usercontact` (
`account` char(30) NOT NULL,
`listName` varchar(60) NOT NULL,
`userList` text NOT NULL,
PRIMARY KEY (`id`)
PRIMARY KEY (`id`),
KEY `account` (`account`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_usergroup`;
CREATE TABLE IF NOT EXISTS `zt_usergroup` (
@@ -675,8 +691,7 @@ CREATE TABLE IF NOT EXISTS `zt_userquery` (
`form` text NOT NULL,
`sql` text NOT NULL,
PRIMARY KEY (`id`),
KEY `account` (`account`),
KEY `module` (`module`)
KEY `query` (`account`, `module`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
-- DROP TABLE IF EXISTS `zt_usertpl`;
CREATE TABLE IF NOT EXISTS `zt_usertpl` (
@@ -745,6 +760,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(1, 'bug', 'delete'),
(1, 'bug', 'deleteTemplate'),
(1, 'bug', 'edit'),
(1, 'bug', 'linkBugs'),
(1, 'bug', 'unlinkBug'),
(1, 'bug', 'export'),
(1, 'bug', 'index'),
(1, 'bug', 'report'),
@@ -947,6 +964,7 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(1, 'story', 'activate'),
(1, 'story', 'batchAssignTo'),
(1, 'story', 'batchChangePlan'),
(1, 'story', 'batchChangeBranch'),
(1, 'story', 'batchChangeStage'),
(1, 'story', 'batchClose'),
(1, 'story', 'batchCreate'),
@@ -957,6 +975,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(1, 'story', 'create'),
(1, 'story', 'delete'),
(1, 'story', 'edit'),
(1, 'story', 'linkStory'),
(1, 'story', 'unlinkStory'),
(1, 'story', 'export'),
(1, 'story', 'report'),
(1, 'story', 'review'),
@@ -998,6 +1018,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(1, 'testcase', 'createBug'),
(1, 'testcase', 'delete'),
(1, 'testcase', 'edit'),
(1, 'testcase', 'linkCases'),
(1, 'testcase', 'unlinkCase'),
(1, 'testcase', 'export'),
(1, 'testcase', 'exportTemplet'),
(1, 'testcase', 'groupCase'),
@@ -1074,6 +1096,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(2, 'bug', 'create'),
(2, 'bug', 'deleteTemplate'),
(2, 'bug', 'edit'),
(2, 'bug', 'linkBugs'),
(2, 'bug', 'unlinkBug'),
(2, 'bug', 'export'),
(2, 'bug', 'index'),
(2, 'bug', 'report'),
@@ -1232,6 +1256,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(3, 'bug', 'create'),
(3, 'bug', 'deleteTemplate'),
(3, 'bug', 'edit'),
(3, 'bug', 'linkBugs'),
(3, 'bug', 'unlinkBug'),
(3, 'bug', 'export'),
(3, 'bug', 'index'),
(3, 'bug', 'report'),
@@ -1349,6 +1375,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(3, 'testcase', 'create'),
(3, 'testcase', 'createBug'),
(3, 'testcase', 'edit'),
(3, 'testcase', 'linkCases'),
(3, 'testcase', 'unlinkCase'),
(3, 'testcase', 'export'),
(3, 'testcase', 'exportTemplet'),
(3, 'testcase', 'groupCase'),
@@ -1416,6 +1444,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(4, 'bug', 'delete'),
(4, 'bug', 'deleteTemplate'),
(4, 'bug', 'edit'),
(4, 'bug', 'linkBugs'),
(4, 'bug', 'unlinkBug'),
(4, 'bug', 'export'),
(4, 'bug', 'index'),
(4, 'bug', 'report'),
@@ -1625,6 +1655,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(5, 'bug', 'create'),
(5, 'bug', 'deleteTemplate'),
(5, 'bug', 'edit'),
(5, 'bug', 'linkBugs'),
(5, 'bug', 'unlinkBug'),
(5, 'bug', 'export'),
(5, 'bug', 'index'),
(5, 'bug', 'report'),
@@ -1759,6 +1791,7 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(5, 'search', 'select'),
(5, 'story', 'activate'),
(5, 'story', 'batchChangePlan'),
(5, 'story', 'batchChangeBranch'),
(5, 'story', 'batchChangeStage'),
(5, 'story', 'batchClose'),
(5, 'story', 'batchCreate'),
@@ -1769,6 +1802,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(5, 'story', 'create'),
(5, 'story', 'delete'),
(5, 'story', 'edit'),
(5, 'story', 'linkStory'),
(5, 'story', 'unlinkStory'),
(5, 'story', 'export'),
(5, 'story', 'report'),
(5, 'story', 'review'),
@@ -1849,6 +1884,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(6, 'bug', 'delete'),
(6, 'bug', 'deleteTemplate'),
(6, 'bug', 'edit'),
(6, 'bug', 'linkBugs'),
(6, 'bug', 'unlinkBug'),
(6, 'bug', 'export'),
(6, 'bug', 'index'),
(6, 'bug', 'report'),
@@ -2056,6 +2093,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(7, 'bug', 'delete'),
(7, 'bug', 'deleteTemplate'),
(7, 'bug', 'edit'),
(7, 'bug', 'linkBugs'),
(7, 'bug', 'unlinkBug'),
(7, 'bug', 'export'),
(7, 'bug', 'index'),
(7, 'bug', 'report'),
@@ -2174,6 +2213,7 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(7, 'story', 'activate'),
(7, 'story', 'batchAssignTo'),
(7, 'story', 'batchChangePlan'),
(7, 'story', 'batchChangeBranch'),
(7, 'story', 'batchChangeStage'),
(7, 'story', 'batchClose'),
(7, 'story', 'batchCreate'),
@@ -2184,6 +2224,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(7, 'story', 'create'),
(7, 'story', 'delete'),
(7, 'story', 'edit'),
(7, 'story', 'linkStory'),
(7, 'story', 'unlinkStory'),
(7, 'story', 'export'),
(7, 'story', 'report'),
(7, 'story', 'review'),
@@ -2264,6 +2306,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(8, 'bug', 'delete'),
(8, 'bug', 'deleteTemplate'),
(8, 'bug', 'edit'),
(8, 'bug', 'linkBugs'),
(8, 'bug', 'unlinkBug'),
(8, 'bug', 'export'),
(8, 'bug', 'index'),
(8, 'bug', 'report'),
@@ -2392,6 +2436,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(8, 'testcase', 'createBug'),
(8, 'testcase', 'delete'),
(8, 'testcase', 'edit'),
(8, 'testcase', 'linkCases'),
(8, 'testcase', 'unlinkCase'),
(8, 'testcase', 'export'),
(8, 'testcase', 'exportTemplet'),
(8, 'testcase', 'groupCase'),
@@ -2601,6 +2647,8 @@ INSERT INTO `zt_grouppriv` (`group`, `module`, `method`) VALUES
(10, 'bug', 'close'),
(10, 'bug', 'create'),
(10, 'bug', 'edit'),
(10, 'bug', 'linkBugs'),
(10, 'bug', 'unlinkBug'),
(10, 'bug', 'index'),
(10, 'bug', 'report'),
(10, 'bug', 'view'),

View File

@@ -1,3 +1,134 @@
2016-03-15 8.1.stable
完成的功能:
1864 调整后台设置计划任务时星号和后面文字的间距
1863 调整版本发布页面的样式
1861 计划需求列表页面增加全屏按钮,点击,隐藏右侧的侧边栏。
1860 计划关联需求页面增加创建需求的链接
1859 研发阶段计算没有考虑任务的关闭原因。
1857 升级到最新的ZUI版本
1856 去掉模块的修正数据功能
1855 恢复桌面提醒工具的下载功能
1854 增加限制ip登录的配置项
1852 ie8下面详情页面有js报错 kindeditor未定义。
1851 计划列表页面把按照详情排序去掉。
1850 同级下面不应该有重名的模块。
1842 项目-任务 前面显示关联需求所在的平台信息
1838 优化邮件内容的样式
1837 调整日志的逻辑只有打开debug的情况下面再记录
1836 检查mysqldump的检查逻辑是否还需要
1834 编辑任务更换项目的时候检查指派人是否在新的项目团队中
1832 提交测试的时候,增加抄送功能
1830 项目需求关联列表支持批量移除需求
1824 优化历史记录中比对的展示
1817 列表页面滚动的时候表头固定
1816 优化列表页面的全选和批量操作按钮
1815 优化需求bug任务用例提交页面的样式
1814 优化备注信息的样式
1813 级别和优先级的图标更换。增强其区分度。
1805 实现mysql表的检查修复功能
1803 调整底部升级到专业版的小图标
1800 优化数据库的SQL索引。
1661 详情页面的上一个,下一个按钮提示用户快捷键
1658 梳理需求任务bug用例之间的相关关系
1516 插件的license支持全局的
1344 搜索表单的保存查询删除查询改用ajax。
1244 美化邮件提醒的模板
1201 增加不兼容浏览器的列表
1192 如果有gd库自动进行压缩
1174 将todo/view/footer.html.php移到todo/common.js
1171 删除的用户名字上没有加横线
1087 调整代码高亮的展示形式
947 将pathFix变量改用DIRECTORY_SEPERATOR的缩写DS
945 处理common和commonmodel的关系和逻辑
722 清除没有使用的语言条目
441 将统计报表程序移至model层
439 重构各个模块的browse方法
377 处理多人同时编辑某一条记录的问题
295 权限分组的时候可以按照部门来选择用户
180 合并我的地盘和用户组织视图中的代码
修复的Bug
804 处理备份还原错误的问题
803 http://ranzhi.5upm.com/story-view-2020.html 打开空白
785 批量编辑任务工时消耗可以写 负数
794 编辑需求任务bug的时候原有内容中的空格不要去掉
787 每日提醒功能,只有测试任务时的邮件标题不全
772 需求评审无乱指派给谁最后结果都是指派给创建人
745 加强dao里面对sql的过滤
750 用例执行页面界面显示不全
790 需求导出csv里面包含html标签
789 项目-版本 关联bug搜索 所属产品下拉表为空
795 bug标题中如果英文双引号和英文逗号或句号连在一起导出csv格式数据会出现截断
801 用例批量执行如果步骤太多无法显示预期
792 后台-回收站 找到删除的用户,点击用户名查看资料获取不到数据
793 产品-需求-待关闭 然后点击需求列表中某个需求的名称页面空白
796 项目关联所有平台提交bug时候选择某一平台无法列出该项目
800 移动web版本登录禅道后点击返回pc 显示 “badRequest”
788 计划任务0不能填写
2015-11-13 7.4.beta
完成的功能:
1770 用户属性中的“加入时间”改成“入职时间”
1696 把数据库缺少字段时候的报错记录到 tmp/php日志中。
1749 统计-组织-员工负责表增加实际可用工作日天数选项
1519 调整批量添加用户页面部门下拉列宽
1630 统计-产品页面当所有产品都没有计划的时候也列出所有产品的列表信息
1631 项目-版本提交测试自动同步版本所属产品信息
1636 创建版本如果项目没有关联产品,出现一个关联页面的链接提示
1640 调整统计报表功能的界面
1695 需求和bug的关闭原因如果是重复的话显示重复的链接
1697 bug的自定义模板检查重复逻辑调整
1699 调整安装页面的首页
1700 详情页面处理连续英文字符的内容
1701 优化浏览器贴图的逻辑
1711 产品增加类型设置
1712 完成产品多分支的数据库设计
1713 实现产品多分支的管理功能
1714 实现产品分支的切换
1715 可以为分支维护独立的模块
1716 添加需求的时候可以选择分支或者平台
1717 添加计划的时候可以选择分支或者平台
1718 添加项目的时候可以选择产品的平台或者分支
1719 为项目关联需求的时候检索逻辑调整
1720 调整需求的研发阶段的计算逻辑
1721 调整需求详情页面的研发阶段的展示
1722 调整需求的发布的计算逻辑和显示
1723 调整需求的等待关闭的的判断逻辑
1724 为发布增加分支的管理
1725 为bug增加分支管理
1726 为用例增加分支管理
1727 为版本增加分支管理
1750 新增需求的时候产品列表不显示已关闭产品
1752 项目创建任务的时候如果只关联一个产品的话,模块列表把产品去掉
1756 发布增加停止维护操作
1757 优化创建bug时候的版本检索逻辑
1758 优化编辑bug时候版本的检索逻辑
1759 优化创建bug时候的指派人列表功能
1761 项目导入任务的时候可以选择显示所有可以导入的任务
1769 发布的遗留bug改为手工关联
1771 然之集成的时候可以提示用户进行绑定
1772 一键安装包对路径的格式进行判断。
1773 调整需求的创建页面
1774 调整bug的创建页面
1776 发信页面的sendcloud按钮样式调整下。
1778 批量添加bug的时候把和需求相关的类型隐藏掉
1779 计划需求列表页面增加转移计划的功能
1780 计划关联需求的搜索结果页面增加排序功能和title提示
1781 需求批量修改计划的时候只列最近的若干个,其他的搜索
1782 按照计划搜索的时候,计划的下拉菜单更改其宽度
1784 计划的需求列表页面的删除图标改为移除图标
修复的BUG
748 后台->二次开发->编辑器->用户-> setMenu等 扩展 API 打不开
749 如果用例比较多测试-版本下面用例列表显示时间会比较长
751 搜索时表单展开时,导出下拉菜单弹出时被遮挡
752 组织权限 产品视图出现2个同名权限
753 组织-用户搜索没有隐藏现在不用的字段信息
754 创建任务页面的抄送人员列表出现 closed
756 后台-备份中设置只保留几天的备份功能只能删除过期的sql不能删除file文件
757 发布检查是否重复的时候不要计算已经删除的发布
758 邮件设置如果出错之后button的状态没有解除
2015-09-18 7.3.stable
完成的功能:
1632 需求、任务、bug、用例、文档的搜索功能的下拉框增加关键字快捷搜索功能

View File

@@ -1 +1 @@
升级,请参考 http://www.zentao.net/article-view-78960.html
升级,请参考 http://www.zentao.net/book/zentaopmshelp/41.html

View File

@@ -0,0 +1,830 @@
<?php
/**
* ZenTaoPHP的baseControl类。
* The baseControl class file of ZenTaoPHP framework.
*
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
/**
* baseControl基类.
* The base class of control.
*
* @package framework
*/
class baseControl
{
/**
* 全局对象 $app。
* The global $app object.
*
* @var object
* @access public
*/
public $app;
/**
* 应用名称 $appName
* The global $appName.
*
* @var string
* @access public
*/
public $appName;
/**
* 全局对象 $config。
* The global $config object.
*
* @var object
* @access public
*/
public $config;
/**
* 全局对象 $lang。
* The global $lang object.
*
* @var object
* @access public
*/
public $lang;
/**
* 全局对象 $dbh数据库连接句柄。
* The global $dbh object, the database connection handler.
*
* @var object
* @access public
*/
public $dbh;
/**
* $dao对象实现sql的拼装和执行。
* The $dao object, used to join sql and excute sql.
*
* @var object
* @access public
*/
public $dao;
/**
* $post对象用户可以通过$this->post->key来引用$_POST变量。
* The $post object, useer can access a post var by $this->post->key.
*
* @var ojbect
* @access public
*/
public $post;
/**
* $get对象用户可以通过$this->get->key来引用$_GET变量。
* The $get object, useer can access a get var by $this->get->key.
*
* @var ojbect
* @access public
*/
public $get;
/**
* $session对象用户可以通过$this->session->key来引用$_SESSION变量。
* The $session object, useer can access a session var by $this->session->key.
*
* @var ojbect
* @access public
*/
public $session;
/**
* $server对象用户可以通过$this->server->key来引用$_SERVER变量。
* The $server object, useer can access a server var by $this->server->key.
*
* @var ojbect
* @access public
*/
public $server;
/**
* $cookie对象用户可以通过$this->cookie->key来引用$_COOKIE变量。
* The $cookie object, useer can access a cookie var by $this->cookie->key.
*
* @var ojbect
* @access public
*/
public $cookie;
/**
* 当前模块的名称。
* The name of current module.
*
* @var string
* @access public
*/
public $moduleName;
/**
* $view用于存放从control传到view视图的数据。
* The vars assigned to the view page.
*
* @var object
* @access public
*/
public $view;
/**
* 视图的类型比如html, json。
* The type of the view, such html, json.
*
* @var string
* @access public
*/
public $viewType;
/**
* 输出到浏览器的内容。
* The content to display.
*
* @var string
* @access public
*/
public $output;
/**
* 客户端设备。
* The client device.
*
* @var string
* @access public
*/
public $clientDevice;
/**
* 不同设备下视图文件的前缀。
* The prefix of view file for mobile or PC.
*
* @var string
* @access public
*/
public $devicePrefix;
/**
* 构造方法。
*
* 1. 将全局变量设为baseControl类的成员变量方便baseControl的派生类调用
* 2. 设置当前模块读取该模块的model类
* 3. 初始化$view视图类。
*
* The construct function.
*
* 1. global the global vars, refer them by the class member such as $this->app.
* 2. set the pathes of current module, and load it's model class.
* 3. auto assign the $lang and $config to the view.
*
* @param string $moduleName
* @param string $methodName
* @param string $appName
* @access public
* @return void
*/
public function __construct($moduleName = '', $methodName = '', $appName = '')
{
/*
* 将全局变量设为baseControl类的成员变量方便baseControl的派生类调用。
* Global the globals, and refer them as a class member.
*/
global $app, $config, $lang, $dbh, $common;
$this->app = $app;
$this->config = $config;
$this->lang = $lang;
$this->dbh = $dbh;
$this->viewType = $this->app->getViewType();
$this->appName = $appName ? $appName : $this->app->getAppName();
/**
* 设置当前模块读取该模块的model类。
* Load the model file auto.
*/
$this->setModuleName($moduleName);
$this->setMethodName($methodName);
$this->loadModel($this->moduleName, $appName);
/**
* 如果客户端是手机的话视图文件增加m.前缀。
* If the clent is mobile, add m. as prefix for view file.
*/
$this->setClientDevice();
$this->setDevicePrefix();
/**
* 初始化$view视图类。
* Init the view vars.
*/
$this->view = new stdclass();
$this->view->app = $app;
$this->view->lang = $lang;
$this->view->config = $config;
$this->view->common = $common;
$this->view->title = '';
/**
* 设置超级变量,从$app引用过来。
* Set super vars.
*/
$this->setSuperVars();
}
//-------------------- Model相关方法(Model related methods) --------------------//
/*
* 设置模块名。
* Set the module name.
*
* @param string $moduleName 模块名,如果为空,则从$app中获取. The module name, if empty, get it from $app.
* @access public
* @return void
*/
public function setModuleName($moduleName = '')
{
$this->moduleName = $moduleName ? strtolower($moduleName) : $this->app->getModuleName();
}
/**
* set the method name.
* 设置方法名。
*
* @param string $methodName 方法名,如果为空,则从$app中获取。The method name, if empty, get it from $app.
* @access public
* @return void
*/
public function setMethodName($methodName = '')
{
$this->methodName = $methodName ? strtolower($methodName) : $this->app->getMethodName();
}
/**
* 加载指定模块的model文件。
* Load the model file of one module.
*
* @param string $moduleName 模块名如果为空使用当前模块。The module name, if empty, use current module's name.
* @param string $appName The app name, if empty, use current app's name.
* @access public
* @return object|bool 如果没有model文件返回false否则返回model对象。If no model file, return false, else return the model object.
*/
public function loadModel($moduleName = '', $appName = '')
{
if(empty($moduleName)) $moduleName = $this->moduleName;
if(empty($appName)) $appName = $this->appName;
$modelFile = helper::setModelFile($moduleName, $appName);
/**
* 如果没有model文件尝试加载config配置信息。
* If no model file, try load config.
*/
if(!helper::import($modelFile))
{
$this->app->loadConfig($moduleName, $appName, false);
$this->app->loadLang($moduleName, $appName);
$this->dao = new dao();
return false;
}
/**
* 如果没有扩展文件model类名是$moduleName + 'model'如果有扩展还需要增加ext前缀。
* If no extension file, model class name is $moduleName + 'model', else with 'ext' as the prefix.
*/
$modelClass = class_exists('ext' . $appName . $moduleName. 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model';
if(!class_exists($modelClass))
{
$modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model';
if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true);
}
/**
* 初始化model对象在control对象中可以通过$this->$moduleName来引用。同时将dao对象赋为control对象的成员变量方便引用。
* Init the model object thus you can try $this->$moduleName to access it. Also assign the $dao object as a member of control object.
*/
$this->$moduleName = new $modelClass($appName);
$this->dao = $this->$moduleName->dao;
return $this->$moduleName;
}
/**
* 设置超级全局变量,方便直接引用。
* Set the super vars.
*
* @access public
* @return void
*/
public function setSuperVars()
{
$this->post = $this->app->post;
$this->get = $this->app->get;
$this->server = $this->app->server;
$this->session = $this->app->session;
$this->cookie = $this->app->cookie;
$this->global = $this->app->global;
}
/**
* 设置客户端的设备类型。
* Set client device.
*
* @access public
* @return void
*/
public function setClientDevice()
{
$this->clientDevice = $this->app->clientDevice;
}
/**
* 如果客户端是手机的话视图文件增加m.前缀。
* If the clent is mobile, add m. as prefix for view file.
*
* @access public
* @return void
*/
public function setDevicePrefix()
{
$this->devicePrefix = zget($this->config->devicePrefix, $this->viewType, '');
}
//-------------------- 视图相关方法(View related methods) --------------------//
/**
* 设置视图文件:主视图文件,扩展视图文件, 站点扩展视图文件,以及钩子脚本。
* Set view files: the main file, extension view file, site extension view file and hook files.
*
* @param string $moduleName module name
* @param string $methodName method name
* @access public
* @return string the view file
*/
public function setViewFile($moduleName, $methodName)
{
$moduleName = strtolower(trim($moduleName));
$methodName = strtolower(trim($methodName));
$modulePath = $this->app->getModulePath($this->appName, $moduleName);
$viewExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'view');
$viewType = $this->viewType == 'mhtml' ? 'html' : $this->viewType;
$mainViewFile = $modulePath . 'view' . DS . $this->devicePrefix . $methodName . '.' . $viewType . '.php';
$commonExtViewFile = $viewExtPath['common'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
$siteExtViewFile = empty($viewExtPath['site']) ? '' : $viewExtPath['site'] . $this->devicePrefix . $methodName . ".{$viewType}.php";
$viewFile = file_exists($commonExtViewFile) ? $commonExtViewFile : $mainViewFile;
$viewFile = (!empty($siteExtViewFile) and file_exists($siteExtViewFile)) ? $siteExtViewFile : $viewFile;
if(!is_file($viewFile)) $this->app->triggerError("the view file $viewFile not found", __FILE__, __LINE__, $exit = true);
$commonExtHookFiles = glob($viewExtPath['common'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
$siteExtHookFiles = empty($viewExtPath['site']) ? '' : glob($viewExtPath['site'] . $this->devicePrefix . $methodName . ".*.{$viewType}.hook.php");
$extHookFiles = array_merge((array) $commonExtHookFiles, (array) $siteExtHookFiles);
if(!empty($extHookFiles)) return array('viewFile' => $viewFile, 'hookFiles' => $extHookFiles);
return $viewFile;
}
/**
* 获取某一个视图文件的扩展。
* Get the extension file of an view.
*
* @param string $viewFile
* @access public
* @return string|bool If extension view file exists, return the path. Else return fasle.
*/
public function getExtViewFile($viewFile)
{
/**
* 首先找sitecode下的扩展文件如果没有再找ext下的扩展文件。
* Find extViewFile in ext/_$siteCode/view first, then try ext/view/.
*/
if($this->config->site->code)
{
$extPath = dirname(dirname(realpath($viewFile))) . "/ext/_{$this->config->site->code}/view";
$extViewFile = $extPath . basename($viewFile);
if(file_exists($extViewFile))
{
helper::cd($extPath);
return $extViewFile;
}
}
$extPath = dirname(dirname(realpath($viewFile))) . '/ext/view/';
$extViewFile = $extPath . basename($viewFile);
if(file_exists($extViewFile))
{
helper::cd($extPath);
return $extViewFile;
}
return false;
}
/**
* 获取适用于当前方法的css该模块公用的css + 当前方法的css + 扩展的css。
* Get css codes applied to current method: module common css + method css + extension css.
*
* @param string $moduleName
* @param string $methodName
* @access public
* @return string
*/
public function getCSS($moduleName, $methodName)
{
$moduleName = strtolower(trim($moduleName));
$methodName = strtolower(trim($methodName));
$modulePath = $this->app->getModulePath($this->appName, $moduleName);
$cssExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'css') ;
$cssMethodExt = $cssExtPath['common'] . $methodName . DS;
$cssCommonExt = $cssExtPath['common'] . 'common' . DS;
$css = '';
$mainCssFile = $modulePath . 'css' . DS . $this->devicePrefix . 'common.css';
$methodCssFile = $modulePath . 'css' . DS . $this->devicePrefix . $methodName . '.css';
if(file_exists($mainCssFile)) $css .= file_get_contents($mainCssFile);
if(is_file($methodCssFile)) $css .= file_get_contents($methodCssFile);
$cssExtFiles = glob($cssCommonExt . $this->devicePrefix . '*.css');
if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile);
$cssExtFiles = glob($cssMethodExt . $this->devicePrefix . '*.css');
if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile);
if(!empty($cssExtPath['site']))
{
$cssMethodExt = $cssExtPath['site'] . $methodName . DS;
$cssCommonExt = $cssExtPath['site'] . 'common' . DS;
$cssExtFiles = glob($cssCommonExt . $this->devicePrefix . '*.css');
if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile);
$cssExtFiles = glob($cssMethodExt . $this->devicePrefix . '*.css');
if(!empty($cssExtFiles) and is_array($cssExtFiles)) foreach($cssExtFiles as $cssFile) $css .= file_get_contents($cssFile);
}
return $css;
}
/**
* 获取适用于当前方法的js该模块公用的js + 当前方法的js + 扩展的js。
* Get js codes applied to current method: module common js + method js + extension js.
*
* @param string $moduleName
* @param string $methodName
* @access public
* @return string
*/
public function getJS($moduleName, $methodName)
{
$moduleName = strtolower(trim($moduleName));
$methodName = strtolower(trim($methodName));
$modulePath = $this->app->getModulePath($this->appName, $moduleName);
$jsExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'js');
$jsMethodExt = $jsExtPath['common'] . $methodName . DS;
$jsCommonExt = $jsExtPath['common'] . 'common' . DS;
$js = '';
$mainJsFile = $modulePath . 'js' . DS . $this->devicePrefix . 'common.js';
$methodJsFile = $modulePath . 'js' . DS . $this->devicePrefix . $methodName . '.js';
if(file_exists($mainJsFile)) $js .= file_get_contents($mainJsFile);
if(is_file($methodJsFile)) $js .= file_get_contents($methodJsFile);
$jsExtFiles = glob($jsCommonExt . $this->devicePrefix . '*.js');
if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile);
$jsExtFiles = glob($jsMethodExt . $this->devicePrefix . '*.js');
if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile);
if(!empty($jsExtPath['site']))
{
$jsMethodExt = $jsExtPath['site'] . $methodName . DS;
$jsCommonExt = $jsExtPath['site'] . 'common' . DS;
$jsExtFiles = glob($jsCommonExt . $this->devicePrefix . '*.js');
if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile);
$jsExtFiles = glob($jsMethodExt . $this->devicePrefix . '*.js');
if(!empty($jsExtFiles) and is_array($jsExtFiles)) foreach($jsExtFiles as $jsFile) $js .= file_get_contents($jsFile);
}
return $js;
}
/**
* 向$view传递一个变量。
* Assign one var to the view vars.
*
* @param string $name the name.
* @param mixed $value the value.
* @access public
* @return void
*/
public function assign($name, $value)
{
$this->view->$name = $value;
}
/**
* 清空$output。
* Clear the output.
*
* @access public
* @return void
*/
public function clear()
{
$this->output = '';
}
/**
* 渲染视图文件。
* Parse view file.
*
* @param string $moduleName module name, if empty, use current module.
* @param string $methodName method name, if empty, use current method.
* @access public
* @return string the parsed result.
*/
public function parse($moduleName = '', $methodName = '')
{
if(empty($moduleName)) $moduleName = $this->moduleName;
if(empty($methodName)) $methodName = $this->methodName;
if($this->viewType == 'json') $this->parseJSON($moduleName, $methodName);
if($this->viewType != 'json') $this->parseDefault($moduleName, $methodName);
return $this->output;
}
/**
* 渲染json格式。
* Parse json format.
*
* @param string $moduleName module name
* @param string $methodName method name
* @access public
* @return void
*/
public function parseJSON($moduleName, $methodName)
{
unset($this->view->app);
unset($this->view->config);
unset($this->view->lang);
unset($this->view->header);
unset($this->view->position);
unset($this->view->moduleTree);
unset($this->view->common);
unset($this->view->pager->app);
unset($this->view->pager->lang);
$output['status'] = is_object($this->view) ? 'success' : 'fail';
$output['data'] = json_encode($this->view);
$output['md5'] = md5(json_encode($this->view));
$this->output = json_encode($output);
}
/**
/**
* 默认渲染方法适用于viewType = html的时候。
* Default parse method when viewType != json, like html.
*
* @param string $moduleName module name
* @param string $methodName method name
* @access public
* @return void
*/
public function parseDefault($moduleName, $methodName)
{
/**
* 设置视图文件。(PHP7有一个bug不能直接$viewFile = $this->setViewFile())。
* Set viewFile. (Can't assign $viewFile = $this->setViewFile() directly because one php7's bug.)
*/
$results = $this->setViewFile($moduleName, $methodName);
$viewFile = $results;
if(is_array($results)) extract($results);
/**
* 获得当前页面的CSS和JS。
* Get css and js codes for current method.
*/
$css = $this->getCSS($moduleName, $methodName);
$js = $this->getJS($moduleName, $methodName);
if($css) $this->view->pageCSS = $css;
if($js) $this->view->pageJS = $js;
/**
* 切换到视图文件所在的目录以保证视图文件里面的include语句能够正常运行。
* Change the dir to the view file to keep the relative pathes work.
*/
$currentPWD = getcwd();
chdir(dirname($viewFile));
/**
* 使用extract安定ob方法渲染$viewFile里面的代码。
* Use extract and ob functions to eval the codes in $viewFile.
*/
extract((array)$this->view);
ob_start();
include $viewFile;
if(isset($hookFiles)) foreach($hookFiles as $hookFile) if(file_exists($hookFile)) include $hookFile;
$this->output .= ob_get_contents();
ob_end_clean();
/**
* 渲染完毕后,再切换回之前的路径。
* At the end, chang the dir to the previous.
*/
chdir($currentPWD);
}
/**
* 获取一个方法的输出内容,这样我们可以在一个方法里获取其他模块方法的内容。
* 如果模块名为空,则调用该模块、该方法;如果设置了模块名,调用指定模块指定方法。
*
* Get the output of one module's one method as a string, thus in one module's method, can fetch other module's content.
* If the module name is empty, then use the current module and method. If set, use the user defined module and method.
*
* @param string $moduleName module name.
* @param string $methodName method name.
* @param array $params params.
* @access public
* @return string the parsed html.
*/
public function fetch($moduleName = '', $methodName = '', $params = array(), $appName = '')
{
if($moduleName == '') $moduleName = $this->moduleName;
if($methodName == '') $methodName = $this->methodName;
if($appName == '') $appName = $this->appName;
if($moduleName == $this->moduleName and $methodName == $this->methodName)
{
$this->parse($moduleName, $methodName);
return $this->output;
}
/**
* 设置引用的文件和路径。
* Set the pathes and files to included.
**/
$modulePath = $this->app->getModulePath($appName, $moduleName);
$moduleControlFile = $modulePath . 'control.php';
$actionExtPath = $this->app->getModuleExtPath($appName, $moduleName, 'control');
$commonActionExtFile = $actionExtPath['common'] . strtolower($methodName) . '.php';
$file2Included = file_exists($commonActionExtFile) ? $commonActionExtFile : $moduleControlFile;
if(!empty($actionExtPath['site']))
{
$siteActionExtFile = $actionExtPath['site'] . strtolower($methodName) . '.php';
$file2Included = file_exists($siteActionExtFile) ? $siteActionExtFile : $file2Included;
}
/**
* 加载控制器文件。
* Load the control file.
*/
if(!is_file($file2Included)) $this->app->triggerError("The control file $file2Included not found", __FILE__, __LINE__, $exit = true);
$currentPWD = getcwd();
chdir(dirname($file2Included));
if($moduleName != $this->moduleName) helper::import($file2Included);
/**
* 设置调用的类名。
* Set the name of the class to be called.
*/
$className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName;
if(!class_exists($className)) $this->app->triggerError(" The class $className not found", __FILE__, __LINE__, $exit = true);
/**
* 解析参数创建模块control对象。
* Parse the params, create the $module control object.
*/
if(!is_array($params)) parse_str($params, $params);
$module = new $className($moduleName, $methodName, $appName);
/**
* 调用对应方法使用ob方法获取输出内容。
* Call the method and use ob function to get the output.
*/
ob_start();
call_user_func_array(array($module, $methodName), $params);
$output = ob_get_contents();
ob_end_clean();
/**
* 返回内容。
* Return the content.
*/
unset($module);
chdir($currentPWD);
return $output;
}
/**
* 向浏览器输出内容。
* Print the content of the view.
*
* @param string $moduleName module name
* @param string $methodName method name
* @access public
* @return void
*/
public function display($moduleName = '', $methodName = '')
{
if(empty($this->output)) $this->parse($moduleName, $methodName);
echo $this->output;
}
/**
* 直接输出data数据通常用于ajax请求中。
* Send data directly, for ajax requests.
*
* @param misc $data
* @param string $type
* @access public
* @return void
*/
public function send($data, $type = 'json')
{
if($type != 'json') die();
$data = (array) $data;
if(helper::isAjaxRequest()) print(json_encode($data)) and die(helper::removeUTF8Bom(ob_get_clean()));
/**
* 响应非ajax的请求。
* Response request not ajax.
*/
if(isset($data['result']) and $data['result'] == 'success')
{
if(!empty($data['message'])) echo js::alert($data['message']);
$locate = isset($data['locate']) ? $data['locate'] : (isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '');
if(!empty($locate)) die(js::locate($locate));
die(isset($data['message']) ? $data['message'] : 'success');
}
if(isset($data['result']) and $data['result'] == 'fail')
{
if(!empty($data['message']))
{
$message = json_decode(json_encode((array)$data['message']));
foreach((array)$message as $item => $errors) $message->$item = implode(',', $errors);
echo js::alert(strip_tags(implode(" ", (array) $message)));
die(js::locate('back'));
}
die('fail');
}
}
/**
* 创建一个模块方法的链接。
* Create a link to one method of one module.
*
* @param string $moduleName module name
* @param string $methodName method name
* @param string|array $vars the params passed, can be array(key=>value) or key1=value1&key2=value2
* @param string $viewType the view type
* @access public
* @return string the link string.
*/
public function createLink($moduleName, $methodName = 'index', $vars = array(), $viewType = '', $onlybody = false)
{
if(empty($moduleName)) $moduleName = $this->moduleName;
return helper::createLink($moduleName, $methodName, $vars, $viewType, $onlybody);
}
/**
* 创建当前模块的一个方法链接。
* Create a link to the inner method of current module.
*
* @param string $methodName method name
* @param string|array $vars the params passed, can be array(key=>value) or key1=value1&key2=value2
* @param string $viewType the view type
* @access public
* @return string the link string.
*/
public function inlink($methodName = 'index', $vars = array(), $viewType = '', $onlybody = false)
{
return helper::createLink($this->moduleName, $methodName, $vars, $viewType, $onlybody);
}
/**
* 重定向到另一个页面。
* Location to another page.
*
* @param string $url the target url.
* @access public
* @return void
*/
public function locate($url)
{
header("location: $url");
exit;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,290 @@
<?php
/**
* ZenTaoPHP的baseModel类。
* The baseModel class file of ZenTaoPHP framework.
*
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
/**
* baseModel基类。
* The base class of model.
*
* @package framework
*/
class baseModel
{
/**
* 全局对象$app。
* The global $app object.
*
* @var object
* @access public
*/
public $app;
/**
* 应用名称$appName。
* The global appName.
*
* @var string
* @access public
*/
public $appName;
/**
* 全局对象$config。
* The global $config object.
*
* @var object
* @access public
*/
public $config;
/**
* 全局对象$lang。
* The global $lang object.
*
* @var object
* @access public
*/
public $lang;
/**
* 全局对象$dbh数据库连接句柄。
* The global $dbh object, the database connection handler.
*
* @var object
* @access public
*/
public $dbh;
/**
* $dao对象用于访问或者更新数据库。
* The $dao object, used to access or update database.
*
* @var object
* @access public
*/
public $dao;
/**
* $post对象用于访问$_POST变量。
* The $post object, used to access the $_POST var.
*
* @var ojbect
* @access public
*/
public $post;
/**
* $get对象用于访问$_GET变量。
* The $get object, used to access the $_GET var.
*
* @var ojbect
* @access public
*/
public $get;
/**
* $session对象用于访问$_SESSION变量。
* The $session object, used to access the $_SESSION var.
*
* @var ojbect
* @access public
*/
public $session;
/**
* $server对象用于访问$_SERVER变量。
* The $server object, used to access the $_SERVER var.
*
* @var ojbect
* @access public
*/
public $server;
/**
* $cookie对象用于访问$_COOKIE变量。
* The $cookie object, used to access the $_COOKIE var.
*
* @var ojbect
* @access public
*/
public $cookie;
/**
* $global对象用于访问$_GLOBAL变量。
* The $global object, used to access the $_GLOBAL var.
*
* @var ojbect
* @access public
*/
public $global;
/**
* 构造方法。
* 1. 将全局变量设为model类的成员变量方便model的派生类调用
* 2. 设置$config, $lang, $dbh, $dao。
*
* The construct function.
* 1. global the global vars, refer them by the class member such as $this->app.
* 2. set the pathes, config, lang of current module
*
* @param string $appName
* @access public
* @return void
*/
public function __construct($appName = '')
{
global $app, $config, $lang, $dbh;
$this->app = $app;
$this->config = $config;
$this->lang = $lang;
$this->dbh = $dbh;
$this->appName = empty($appName) ? $this->app->getAppName() : $appName;
$moduleName = $this->getModuleName();
$this->app->loadLang($moduleName, $this->appName);
$this->app->loadConfig($moduleName, $this->appName, $exitIfNone = false);
$this->loadDAO();
$this->setSuperVars();
}
/**
* 获取该model的模块名而不是用户请求的模块名。
*
* 这个方法通过去掉该model类名的'ext'和'model'字符串,来获取当前模块名。
* 不要使用$app->getModuleName(),因为其返回的是用户请求的模块名。
* 另一个model可以通过loadModel()加载进来,与请求的模块名不一致。
*
* Get the module name of this model. Not the module user visiting.
*
* This method replace the 'ext' and 'model' string from the model class name, thus get the module name.
* Not using $app->getModuleName() because it return the module user is visiting. But one module can be
* loaded by loadModel() so we must get the module name of this model.
*
* @access public
* @return string the module name.
*/
public function getModuleName()
{
$parentClass = get_parent_class($this);
$selfClass = get_class($this);
$className = $parentClass == 'model' ? $selfClass : $parentClass;
if($className == 'extensionModel') return 'extension';
return strtolower(str_ireplace(array('ext', 'Model'), '', $className));
}
/**
* 设置全局超级变量。
* Set the super vars.
*
* @access public
* @return void
*/
public function setSuperVars()
{
$this->post = $this->app->post;
$this->get = $this->app->get;
$this->server = $this->app->server;
$this->cookie = $this->app->cookie;
$this->session = $this->app->session;
$this->global = $this->app->global;
}
/**
* 加载一个模块的model。加载完成后使用$this->$moduleName来访问这个model对象。
* 比如loadModel('user')引入user模块的model实例对象可以通过$this->user来访问它。
*
* Load the model of one module. After loaded, can use $this->$moduleName to visit the model object.
*
* @param string $moduleName
* @access public
* @return object|bool the model object or false if model file not exists.
*/
public function loadModel($moduleName, $appName = '')
{
if(empty($moduleName)) return false;
if(empty($appName)) $appName = $this->appName;
$modelFile = helper::setModelFile($moduleName, $appName);
if(!helper::import($modelFile)) return false;
$modelClass = class_exists('ext' . $appName . $moduleName. 'model') ? 'ext' . $appName . $moduleName . 'model' : $appName . $moduleName . 'model';
if(!class_exists($modelClass))
{
$modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model';
if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true);
}
$this->$moduleName = new $modelClass($appName);
return $this->$moduleName;
}
/**
* 加载model的class扩展。
* Load extension class of a model. Saved to $moduleName/ext/model/class/$extensionName.class.php.
*
* @param string $extensionName
* @param string $moduleName
* @access public
* @return void
*/
public function loadExtension($extensionName, $moduleName = '')
{
if(empty($extensionName)) return false;
/* Set extenson name and extension file. */
$extensionName = strtolower($extensionName);
$moduleName = $moduleName ? $moduleName : $this->getModuleName();
$moduleExtPath = $this->app->getModuleExtPath($this->appName, $moduleName, 'model');
if(!empty($moduleExtPath['site']))$extensionFile = $moduleExtPath['site'] . 'class/' . $extensionName . '.class.php';
if(!isset($extensionFile) or !file_exists($extensionFile)) $extensionFile = $moduleExtPath['common'] . 'class/' . $extensionName . '.class.php';
/* Try to import parent model file auto and then import the extension file. */
if(!class_exists($moduleName . 'Model')) helper::import($this->app->getModulePath($this->appName, $moduleName) . 'model.php');
if(!helper::import($extensionFile)) return false;
/* Set the extension class name. */
$extensionClass = $extensionName . ucfirst($moduleName);
if(!class_exists($extensionClass)) return false;
/* Create an instance of the extension class and return it. */
$extensionObject = new $extensionClass;
$extensionClass = str_replace('Model', '', $extensionClass);
$this->$extensionClass = $extensionObject;
return $extensionObject;
}
/**
* 加载DAO。
* Load DAO.
*
* @access public
* @return void
*/
public function loadDAO()
{
$this->dao = $this->app->loadClass('dao');
}
/**
* 删除记录
* Delete one record.
*
* @param string $table the table name
* @param string $id the id value of the record to be deleted
* @access public
* @return void
*/
public function delete($table, $id)
{
$this->dao->delete()->from($table)->where('id')->eq($id)->exec();
}
}

File diff suppressed because it is too large Load Diff

631
framework/control.class.php Executable file → Normal file
View File

@@ -1,640 +1,23 @@
<?php
/**
* ZenTaoPHP的control类。
* The control class file of ZenTaoPHP framework.
*
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
/**
* The base class of control.
*
* control基类继承与baseControl所有模块的control类都派生于它。
* The base class of control extends baseControl.
*
* @package framework
*/
class control
include dirname(__FILE__) . '/base/control.class.php';
class control extends baseControl
{
/**
* The global $app object.
*
* @var object
* @access protected
*/
protected $app;
/**
* The global $config object.
*
* @var object
* @access protected
*/
protected $config;
/**
* The global $lang object.
*
* @var object
* @access protected
*/
protected $lang;
/**
* The global $dbh object, the database connection handler.
*
* @var object
* @access protected
*/
protected $dbh;
/**
* The $dao object, used to access or update database.
*
* @var object
* @access protected
*/
public $dao;
/**
* The $post object, used to access the $_POST var.
*
* @var ojbect
* @access public
*/
public $post;
/**
* The $get object, used to access the $_GET var.
*
* @var ojbect
* @access public
*/
public $get;
/**
* The $session object, used to access the $_SESSION var.
*
* @var ojbect
* @access public
*/
public $session;
/**
* The $server object, used to access the $_SERVER var.
*
* @var ojbect
* @access public
*/
public $server;
/**
* The $cookie object, used to access the $_COOKIE var.
*
* @var ojbect
* @access public
*/
public $cookie;
/**
* The $global object, used to access the $_GLOBAL var.
*
* @var ojbect
* @access public
*/
public $global;
/**
* The name of current module.
*
* @var string
* @access protected
*/
protected $moduleName;
/**
* The vars assigned to the view page.
*
* @var object
* @access public
*/
public $view;
/**
* The type of the view, such html, json.
*
* @var string
* @access private
*/
private $viewType;
/**
* The content to display.
*
* @var string
* @access private
*/
private $output;
/**
* The directory seperator.
*
* @var string
* @access protected
*/
protected $pathFix;
/**
* The prefix of view file for mobile or PC.
*
* @var string
* @access public
*/
public $viewPrefix;
/**
* The construct function.
*
* 1. global the global vars, refer them by the class member such as $this->app.
* 2. set the pathes of current module, and load it's mode class.
* 3. auto assign the $lang and $config to the view.
*
* @access public
* @return void
*/
public function __construct($moduleName = '', $methodName = '')
{
/* Global the globals, and refer them to the class member. */
global $app, $config, $lang, $dbh;
$this->app = $app;
$this->config = $config;
$this->lang = $lang;
$this->dbh = $dbh;
$this->pathFix = $this->app->getPathFix();
$this->viewType = $this->app->getViewType();
/* Load the model file auto. */
$this->setModuleName($moduleName);
$this->setMethodName($methodName);
$this->loadModel();
$this->setViewPrefix();
/* Init the view vars. */
$this->view = new stdclass();
$this->view->app = $app;
$this->view->lang = $lang;
$this->view->config = $config;
$this->view->title = '';
/* Set super vars. */
$this->setSuperVars();
}
//-------------------- Model related methods --------------------//
/* Set the module name.
*
* @param string $moduleName The module name, if empty, get it from $app.
* @access private
* @return void
*/
private function setModuleName($moduleName = '')
{
$this->moduleName = $moduleName ? strtolower($moduleName) : $this->app->getModuleName();
}
/* Set the method name.
*
* @param string $methodName The method name, if empty, get it from $app.
* @access private
* @return void
*/
private function setMethodName($methodName = '')
{
$this->methodName = $methodName ? strtolower($methodName) : $this->app->getMethodName();
}
/**
* Load the model file of one module.
*
* @param string $methodName The method name, if empty, use current module's name.
* @access public
* @return object|bool If no model file, return false. Else return the model object.
*/
public function loadModel($moduleName = '')
{
if(empty($moduleName)) $moduleName = $this->moduleName;
$modelFile = helper::setModelFile($moduleName);
/* If no model file, try load config. */
if(!helper::import($modelFile))
{
$this->app->loadConfig($moduleName, false);
$this->app->loadLang($moduleName);
$this->dao = new dao();
return false;
}
$modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model';
if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true);
$this->$moduleName = new $modelClass();
$this->dao = $this->$moduleName->dao;
return $this->$moduleName;
}
/**
* Set the super vars.
*
* @access protected
* @return void
*/
protected function setSuperVars()
{
$this->post = $this->app->post;
$this->get = $this->app->get;
$this->server = $this->app->server;
$this->session = $this->app->session;
$this->cookie = $this->app->cookie;
$this->global = $this->app->global;
}
/**
* Set the prefix of view file for mobile or PC.
*
* @access public
* @return void
*/
public function setViewPrefix()
{
$this->viewPrefix = '';
if(isset($this->config->viewPrefix[$this->viewType])) $this->viewPrefix = $this->config->viewPrefix[$this->viewType];
}
//-------------------- View related methods --------------------//
/**
* Set the view file, thus can use fetch other module's page.
*
* @param string $moduleName module name
* @param string $methodName method name
* @access private
* @return string the view file
*/
private function setViewFile($moduleName, $methodName)
{
$moduleName = strtolower(trim($moduleName));
$methodName = strtolower(trim($methodName));
$modulePath = $this->app->getModulePath($moduleName);
$viewExtPath = $this->app->getModuleExtPath($moduleName, 'view');
/* Set infix for view file in mobile or pc. */
$viewType = $this->viewType;
if(isset($this->config->viewPrefix[$this->viewType])) $viewType = 'html';
/* The main view file, extension view file and hook file. */
$mainViewFile = $modulePath . 'view' . $this->pathFix . $this->viewPrefix . $methodName . '.' . $viewType . '.php';
$extViewFile = $viewExtPath . $this->viewPrefix . $methodName . ".{$viewType}.php";
$extHookFiles = glob($viewExtPath . $this->viewPrefix . $methodName . ".*.{$viewType}.hook.php");
$viewFile = file_exists($extViewFile) ? $extViewFile : $mainViewFile;
if(!is_file($viewFile)) $this->app->triggerError("the view file $viewFile not found", __FILE__, __LINE__, $exit = true);
if(!empty($extHookFiles)) return array('viewFile' => $viewFile, 'hookFiles' => $extHookFiles);
return $viewFile;
}
/**
* Get the extension file of an view.
*
* @param string $viewFile
* @access public
* @return string|bool If extension view file exists, return the path. Else return fasle.
*/
public function getExtViewFile($viewFile)
{
$extPath = dirname(dirname(realpath($viewFile))) . '/ext/view/';
$extViewFile = $extPath . basename($viewFile);
if(file_exists($extViewFile))
{
helper::cd($extPath);
return $extViewFile;
}
return false;
}
/**
* Get css code for a method.
*
* @param string $moduleName
* @param string $methodName
* @access private
* @return string
*/
private function getCSS($moduleName, $methodName)
{
$moduleName = strtolower(trim($moduleName));
$methodName = strtolower(trim($methodName));
$modulePath = $this->app->getModulePath($moduleName);
$cssMethodExt = $this->app->getModuleExtPath($moduleName, 'css') . $methodName . $this->pathFix;
$cssCommonExt = $this->app->getModuleExtPath($moduleName, 'css') . 'common' . $this->pathFix;
$css = '';
$mainCssFile = $modulePath . 'css' . $this->pathFix . $this->viewPrefix . 'common.css';
$methodCssFile = $modulePath . 'css' . $this->pathFix . $this->viewPrefix . $methodName . '.css';
if(file_exists($mainCssFile)) $css .= file_get_contents($mainCssFile);
if(is_file($methodCssFile)) $css .= file_get_contents($methodCssFile);
$cssExtFiles = glob($cssCommonExt . $this->viewPrefix . '*.css');
if(is_array($cssExtFiles))
{
foreach($cssExtFiles as $cssFile)
{
$css .= file_get_contents($cssFile);
}
}
$cssExtFiles = glob($cssMethodExt . $this->viewPrefix . '*.css');
if(is_array($cssExtFiles))
{
foreach($cssExtFiles as $cssFile)
{
$css .= file_get_contents($cssFile);
}
}
return $css;
}
/**
* Get js code for a method.
*
* @param string $moduleName
* @param string $methodName
* @access private
* @return string
*/
private function getJS($moduleName, $methodName)
{
$moduleName = strtolower(trim($moduleName));
$methodName = strtolower(trim($methodName));
$modulePath = $this->app->getModulePath($moduleName);
$jsMethodExt = $this->app->getModuleExtPath($moduleName, 'js') . $methodName . $this->pathFix;
$jsCommonExt = $this->app->getModuleExtPath($moduleName, 'js') . 'common' . $this->pathFix;
$js = '';
$mainJsFile = $modulePath . 'js' . $this->pathFix . $this->viewPrefix . 'common.js';
$methodJsFile = $modulePath . 'js' . $this->pathFix . $this->viewPrefix . $methodName . '.js';
if(file_exists($mainJsFile)) $js .= file_get_contents($mainJsFile);
if(is_file($methodJsFile)) $js .= file_get_contents($methodJsFile);
$jsExtFiles = glob($jsCommonExt . $this->viewPrefix . '*.js');
if(is_array($jsExtFiles))
{
foreach($jsExtFiles as $jsFile)
{
$js .= file_get_contents($jsFile);
}
}
$jsExtFiles = glob($jsMethodExt . $this->viewPrefix . '*.js');
if(is_array($jsExtFiles))
{
foreach($jsExtFiles as $jsFile)
{
$js .= file_get_contents($jsFile);
}
}
return $js;
}
/**
* Assign one var to the view vars.
*
* @param string $name the name.
* @param mixed $value the value.
* @access public
* @return void
*/
public function assign($name, $value)
{
$this->view->$name = $value;
}
/**
* Clear the output.
*
* @access public
* @return void
*/
public function clear()
{
$this->output = '';
}
/**
* Parse view file.
*
* @param string $moduleName module name, if empty, use current module.
* @param string $methodName method name, if empty, use current method.
* @access public
* @return string the parsed result.
*/
public function parse($moduleName = '', $methodName = '')
{
if(empty($moduleName)) $moduleName = $this->moduleName;
if(empty($methodName)) $methodName = $this->methodName;
if($this->viewType == 'json')
{
$this->parseJSON($moduleName, $methodName);
}
else
{
$this->parseDefault($moduleName, $methodName);
}
return $this->output;
}
/**
* Parse json format.
*
* @param string $moduleName module name
* @param string $methodName method name
* @access private
* @return void
*/
private function parseJSON($moduleName, $methodName)
{
unset($this->view->app);
unset($this->view->config);
unset($this->view->lang);
unset($this->view->header);
unset($this->view->position);
unset($this->view->moduleTree);
$output['status'] = is_object($this->view) ? 'success' : 'fail';
$output['data'] = json_encode($this->view);
$output['md5'] = md5(json_encode($this->view));
$this->output = json_encode($output);
}
/**
* Parse default html format.
*
* @param string $moduleName module name
* @param string $methodName method name
* @access private
* @return void
*/
private function parseDefault($moduleName, $methodName)
{
/* Set the view file. */
$viewFile = $this->setViewFile($moduleName, $methodName);
if(is_array($viewFile)) extract($viewFile);
/* Get css and js. */
$css = $this->getCSS($moduleName, $methodName);
$js = $this->getJS($moduleName, $methodName);
if($css) $this->view->pageCss = $css;
if($js) $this->view->pageJS = $js;
/* Change the dir to the view file to keep the relative pathes work. */
$currentPWD = getcwd();
chdir(dirname($viewFile));
extract((array)$this->view);
ob_start();
include $viewFile;
if(isset($hookFiles)) foreach($hookFiles as $hookFile) include $hookFile;
$this->output .= ob_get_contents();
ob_end_clean();
/* At the end, chang the dir to the previous. */
chdir($currentPWD);
}
/**
* Get the output of one module's one method as a string, thus in one module's method, can fetch other module's content.
*
* If the module name is empty, then use the current module and method. If set, use the user defined module and method.
*
* @param string $moduleName module name.
* @param string $methodName method name.
* @param array $params params.
* @access public
* @return string the parsed html.
*/
public function fetch($moduleName = '', $methodName = '', $params = array())
{
if($moduleName == '') $moduleName = $this->moduleName;
if($methodName == '') $methodName = $this->methodName;
if($moduleName == $this->moduleName and $methodName == $this->methodName)
{
$this->parse($moduleName, $methodName);
return $this->output;
}
/* Set the pathes and files to included. */
$modulePath = $this->app->getModulePath($moduleName);
$moduleControlFile = $modulePath . 'control.php';
$actionExtFile = $this->app->getModuleExtPath($moduleName, 'control') . strtolower($methodName) . '.php';
$file2Included = file_exists($actionExtFile) ? $actionExtFile : $moduleControlFile;
/* Load the control file. */
if(!is_file($file2Included)) $this->app->triggerError("The control file $file2Included not found", __FILE__, __LINE__, $exit = true);
$currentPWD = getcwd();
chdir(dirname($file2Included));
if($moduleName != $this->moduleName) helper::import($file2Included);
/* Set the name of the class to be called. */
$className = class_exists("my$moduleName") ? "my$moduleName" : $moduleName;
if(!class_exists($className)) $this->app->triggerError(" The class $className not found", __FILE__, __LINE__, $exit = true);
/* Parse the params, create the $module control object. */
if(!is_array($params)) parse_str($params, $params);
$module = new $className($moduleName, $methodName);
/* Call the method and use ob function to get the output. */
ob_start();
call_user_func_array(array($module, $methodName), $params);
$output = ob_get_contents();
ob_end_clean();
/* Return the content. */
unset($module);
chdir($currentPWD);
return $output;
}
/**
* Print the content of the view.
*
* @param string $moduleName module name
* @param string $methodName method name
* @access public
* @return void
*/
public function display($moduleName = '', $methodName = '')
{
if(empty($this->output)) $this->parse($moduleName, $methodName);
echo $this->output;
}
/**
* Send data directly, for ajax requests.
*
* @param misc $data
* @param string $type
* @access public
* @return void
*/
public function send($data, $type = 'json')
{
if($type == 'json') echo json_encode($data);
die(helper::removeUTF8Bom(ob_get_clean()));
}
/**
* Create a link to one method of one module.
*
* @param string $moduleName module name
* @param string $methodName method name
* @param string|array $vars the params passed, can be array(key=>value) or key1=value1&key2=value2
* @param string $viewType the view type
* @access public
* @return string the link string.
*/
public function createLink($moduleName, $methodName = 'index', $vars = array(), $viewType = '', $onlybody = false)
{
if(empty($moduleName)) $moduleName = $this->moduleName;
return helper::createLink($moduleName, $methodName, $vars, $viewType, $onlybody);
}
/**
* Create a link to the inner method of current module.
*
* @param string $methodName method name
* @param string|array $vars the params passed, can be array(key=>value) or key1=value1&key2=value2
* @param string $viewType the view type
* @access public
* @return string the link string.
*/
public function inlink($methodName = 'index', $vars = array(), $viewType = '', $onlybody = false)
{
return helper::createLink($this->moduleName, $methodName, $vars, $viewType, $onlybody);
}
/**
* Location to another page.
*
* @param string $url the target url.
* @access public
* @return void
*/
public function locate($url)
{
header("location: $url");
exit;
}
}

View File

@@ -1,618 +1,23 @@
<?php
/**
* ZenTaoPHP的helper类。
* The helper class file of ZenTaoPHP framework.
*
* The author disclaims copyright to this source code. In place of
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
/**
* 该类实现了一些常用的方法
* The helper class, contains the tool functions.
*
* @package framework
*/
class helper
include dirname(__FILE__) . '/base/helper.class.php';
class helper extends baseHelper
{
/**
* Set the member's value of one object.
* <code>
* <?php
* $lang->db->user = 'wwccss';
* helper::setMember('lang', 'db.user', 'chunsheng.wang');
* ?>
* </code>
* @param string $objName the var name of the object.
* @param string $key the key of the member, can be parent.child.
* @param mixed $value the value to be set.
* @static
* @access public
* @return bool
*/
static public function setMember($objName, $key, $value)
{
global $$objName;
if(!is_object($$objName) or empty($key)) return false;
$key = str_replace('.', '->', $key);
$value = serialize($value);
$code = ("\$${objName}->{$key}=unserialize(<<<EOT\n$value\nEOT\n);");
eval($code);
return true;
}
/**
* Create a link to a module's method.
*
* This method also mapped in control class to call conveniently.
* <code>
* <?php
* helper::createLink('hello', 'index', 'var1=value1&var2=value2');
* helper::createLink('hello', 'index', array('var1' => 'value1', 'var2' => 'value2');
* ?>
* </code>
* @param string $moduleName module name
* @param string $methodName method name
* @param string|array $vars the params passed to the method, can be array('key' => 'value') or key1=value1&key2=value2) or key1=value1&key2=value2
* @param string $viewType the view type
* @param string $onlybody the view type
* @static
* @access public
* @return string the link string.
*/
static public function createLink($moduleName, $methodName = 'index', $vars = '', $viewType = '', $onlybody = false)
{
global $app, $config;
$link = $config->requestType == 'PATH_INFO' ? $config->webRoot : $_SERVER['PHP_SELF'];
/* Set the view type and vars. */
if(empty($viewType)) $viewType = $app->getViewType();
if(!is_array($vars)) parse_str($vars, $vars);
/* The PATH_INFO type. */
if($config->requestType == 'PATH_INFO')
{
/* If the method equal the default method defined in the config file and the vars is empty, convert the link. */
if($methodName == $config->default->method and empty($vars))
{
/* If the module also equal the default module, change index-index to index.html. */
if($moduleName == $config->default->module)
{
$link .= 'index.' . $viewType;
}
else
{
$link .= $moduleName . '/';
}
}
else
{
$link .= "$moduleName{$config->requestFix}$methodName";
if($config->pathType == 'full')
{
foreach($vars as $key => $value) $link .= "{$config->requestFix}$key{$config->requestFix}$value";
}
else
{
foreach($vars as $value) $link .= "{$config->requestFix}$value";
}
$link .= '.' . $viewType;
}
}
elseif($config->requestType == 'GET')
{
$link .= "?{$config->moduleVar}=$moduleName&{$config->methodVar}=$methodName";
if($viewType != 'html') $link .= "&{$config->viewVar}=" . $viewType;
foreach($vars as $key => $value) $link .= "&$key=$value";
}
/* if page has onlybody param then add this param in all link. the param hide header and footer. */
if($onlybody or isonlybody())
{
$onlybody = $config->requestType == 'PATH_INFO' ? "?onlybody=yes" : "&onlybody=yes";
$link .= $onlybody;
}
return $link;
}
/**
* Import a file instend of include or requie.
*
* @param string $file the file to be imported.
* @static
* @access public
* @return bool
*/
static public function import($file)
{
static $includedFiles = array();
if(!isset($includedFiles[$file]))
{
$return = include $file;
if(!$return) return false;
$includedFiles[$file] = true;
return true;
}
return true;
}
/**
* Set the model file of one module. If there's an extension file, merge it with the main model file.
*
* @param string $moduleName the module name
* @static
* @access public
* @return string the model file
*/
static public function setModelFile($moduleName)
{
global $app;
/* Set the main model file and extension and hook pathes and files. */
$mainModelFile = $app->getModulePath($moduleName) . 'model.php';
$modelExtPath = $app->getModuleExtPath($moduleName, 'model');
$modelHookPath = $modelExtPath . 'hook/';
$extFiles = helper::ls($modelExtPath, '.php');
$hookFiles = helper::ls($modelHookPath, '.php');
/* If no extension files and no hook files, return the main file directly. */
if(empty($extFiles) and empty($hookFiles)) return $mainModelFile;
/* Else, judge whether needed update or not .*/
$needUpdate = false;
$mergedModelFile = $app->getTmpRoot() . 'model' . $app->getPathFix() . $moduleName . '.php';
$lastTime = file_exists($mergedModelFile) ? filemtime($mergedModelFile) : 0;
while(!$needUpdate)
{
foreach($extFiles as $extFile) if(filemtime($extFile) > $lastTime) break 2;
foreach($hookFiles as $hookFile) if(filemtime($hookFile) > $lastTime) break 2;
if(is_dir($modelExtPath ) and filemtime($modelExtPath) > $lastTime) break;
if(is_dir($modelHookPath) and filemtime($modelHookPath) > $lastTime) break;
if(filemtime($mainModelFile) > $lastTime) break;
return $mergedModelFile;
}
/* If loaded zend opcache module, turn off cache when create tmp model file to avoid the conflics. */
if(extension_loaded('Zend OPcache')) ini_set('opcache.enable', 0);
/* Update the cache file. */
$modelClass = $moduleName . 'Model';
$extModelClass = 'ext' . $modelClass;
$extTmpModelClass = 'tmpExt' . $modelClass;
$modelLines = "<?php\n";
$modelLines .= "helper::import('$mainModelFile');\n";
$modelLines .= "class $extTmpModelClass extends $modelClass \n{\n";
/* Cycle all the extension files. */
foreach($extFiles as $extFile)
{
$extLines = self::removeTagsOfPHP($extFile);
$modelLines .= $extLines . "\n";
}
/* Create the merged model file and import it. */
$replaceMark = '//**//'; // This mark is for replacing code using.
$modelLines .= "$replaceMark\n}";
if(!@file_put_contents($mergedModelFile, $modelLines))
{
die("ERROR: $mergedModelFile not writable, please make sure the " . dirname($mergedModelFile) . ' directory exists and writable');
}
if(!class_exists($extTmpModelClass))include $mergedModelFile;
/* Get hook codes need to merge. */
$hookCodes = array();
foreach($hookFiles as $hookFile)
{
$fileName = baseName($hookFile);
list($method) = explode('.', $fileName);
$hookCodes[$method][] = self::removeTagsOfPHP($hookFile);
}
/* Cycle the hook methods and merge hook codes. */
$hookedMethods = array_keys($hookCodes);
$mainModelCodes = file($mainModelFile);
$mergedModelCodes = file($mergedModelFile);
foreach($hookedMethods as $method)
{
/* Reflection the hooked method to get it's defined position. */
$methodRelfection = new reflectionMethod($extTmpModelClass, $method);
$definedFile = $methodRelfection->getFileName();
$startLine = $methodRelfection->getStartLine() . ' ';
$endLine = $methodRelfection->getEndLine() . ' ';
/* Merge hook codes. */
$oldCodes = $definedFile == $mergedModelFile ? $mergedModelCodes : $mainModelCodes;
$oldCodes = join("", array_slice($oldCodes, $startLine - 1, $endLine - $startLine + 1));
$openBrace = strpos($oldCodes, '{');
$newCodes = substr($oldCodes, 0, $openBrace + 1) . "\n" . join("\n", $hookCodes[$method]) . substr($oldCodes, $openBrace + 1);
/* Replace it. */
if($definedFile == $mergedModelFile)
{
$modelLines = str_replace($oldCodes, $newCodes, $modelLines);
}
else
{
$modelLines = str_replace($replaceMark, $newCodes . "\n$replaceMark", $modelLines);
}
}
/* Save it. */
$modelLines = str_replace($extTmpModelClass, $extModelClass, $modelLines);
file_put_contents($mergedModelFile, $modelLines);
return $mergedModelFile;
}
/**
* Remove tags of PHP
*
* @param string $fileName
* @static
* @access public
* @return string
*/
static public function removeTagsOfPHP($fileName)
{
$code = trim(file_get_contents($fileName));
if(strpos($code, '<?php') === 0) $code = ltrim($code, '<?php');
if(strrpos($code, '?>') !== false) $code = rtrim($code, '?>');
return trim($code);
}
/**
* Create the in('a', 'b') string.
*
* @param string|array $ids the id lists, can be a array or a string with ids joined with comma.
* @static
* @access public
* @return string the string like IN('a', 'b').
*/
static public function dbIN($ids)
{
if(is_array($ids)) return "IN ('" . join("','", $ids) . "')";
return "IN ('" . str_replace(',', "','", str_replace(' ', '',$ids)) . "')";
}
/**
* Create safe base64 encoded string for the framework.
*
* @param string $string the string to encode.
* @static
* @access public
* @return string encoded string.
*/
static public function safe64Encode($string)
{
return strtr(base64_encode($string), '/', '.');
}
/**
* Decode the string encoded by safe64Encode.
*
* @param string $string the string to decode
* @static
* @access public
* @return string decoded string.
*/
static public function safe64Decode($string)
{
return base64_decode(strtr($string, '.', '/'));
}
/**
* Judge a string is utf-8 or not.
*
* @param string $string
* @author hmdker@gmail.com
* @see http://php.net/manual/en/function.mb-detect-encoding.php
* @static
* @access public
* @return bool
*/
static public function isUTF8($string)
{
$c = 0;
$b = 0;
$bits = 0;
$len = strlen($string);
for($i=0; $i<$len; $i++)
{
$c = ord($string[$i]);
if($c > 128)
{
if(($c >= 254)) return false;
elseif($c >= 252) $bits=6;
elseif($c >= 248) $bits=5;
elseif($c >= 240) $bits=4;
elseif($c >= 224) $bits=3;
elseif($c >= 192) $bits=2;
else return false;
if(($i+$bits) > $len) return false;
while($bits > 1)
{
$i++;
$b=ord($string[$i]);
if($b < 128 || $b > 191) return false;
$bits--;
}
}
}
return true;
}
/**
* Compute the diff days of two date.
*
* @param date $date1 the first date.
* @param date $date2 the sencode date.
* @access public
* @return int the diff of the two days.
*/
static public function diffDate($date1, $date2)
{
return round((strtotime($date1) - strtotime($date2)) / 86400, 0);
}
/**
* Get now time use the DT_DATETIME1 constant defined in the lang file.
*
* @access public
* @return datetime now
*/
static public function now()
{
return date(DT_DATETIME1);
}
/**
* Get today according to the DT_DATE1 constant defined in the lang file.
*
* @access public
* @return date today
*/
static public function today()
{
return date(DT_DATE1);
}
/**
* Judge a date is zero or not.
*
* @access public
* @return bool
*/
static public function isZeroDate($date)
{
return substr($date, 0, 4) == '0000';
}
/**
* Get files match the pattern under one directory.
*
* @access public
* @return array the files match the pattern
*/
static public function ls($dir, $pattern = '')
{
$files = array();
$dir = realpath($dir);
if(is_dir($dir)) $files = glob($dir . DIRECTORY_SEPARATOR . '*' . $pattern);
return empty($files) ? array() : $files;
}
/**
* Change directory.
*
* @param string $path
* @static
* @access public
* @return void
*/
static function cd($path = '')
{
static $cwd = '';
if($path)
{
$cwd = getcwd();
chdir($path);
}
else
{
chdir($cwd);
}
}
/**
* Remove UTF8 Bom
*
* @param string $string
* @access public
* @return string
*/
public static function removeUTF8Bom($string)
{
if(substr($string, 0, 3) == pack('CCC', 239, 187, 191)) return substr($string, 3);
return $string;
}
/**
* Check is ajax request.
*
* @static
* @access public
* @return bool
*/
public static function isAjaxRequest()
{
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest';
}
/**
* Set viewType.
*
* @static
* @access public
* @return void
*/
public static function setViewType()
{
global $config, $app;
if($config->requestType == 'PATH_INFO')
{
$pathInfo = $app->getPathInfo('PATH_INFO');
if(empty($pathInfo)) $pathInfo = $app->getPathInfo('ORIG_PATH_INFO');
if(!empty($pathInfo))
{
$dotPos = strrpos($pathInfo, '.');
if($dotPos)
{
$viewType = substr($pathInfo, $dotPos + 1);
}
else
{
$config->default->view = $config->default->view == 'mhtml' ? 'html' : $config->default->view;
}
}
}
elseif($config->requestType == 'GET')
{
if(isset($_GET[$config->viewVar]))
{
$viewType = $_GET[$config->viewVar];
}
else
{
/* Set default view when url has not module name. such as only domain. */
$config->default->view = ($config->default->view == 'mhtml' and isset($_GET[$config->moduleVar])) ? 'html' : $config->default->view;
}
}
if(isset($viewType) and strpos($config->views, ',' . $viewType . ',') === false) $viewType = $config->default->view;
$app->viewType = isset($viewType) ? $viewType : $config->default->view;
}
}
/**
* The short alias of helper::createLink() method.
*
* @param string $methodName the method name
* @param string|array $vars the params passed to the method, can be array('key' => 'value') or key1=value1&key2=value2)
* @param string $viewType
* @return string the link string.
*/
function inLink($methodName = 'index', $vars = '', $viewType = '')
{
global $app;
return helper::createLink($app->getModuleName(), $methodName, $vars, $viewType);
}
/**
* Static cycle a array
*
* @param array $items the array to be cycled.
* @return mixed
*/
function cycle($items)
{
static $i = 0;
if(!is_array($items)) $items = explode(',', $items);
if(!isset($items[$i])) $i = 0;
return $items[$i++];
}
/**
* Get current microtime.
*
* @access protected
* @return float current time.
*/
function getTime()
{
list($usec, $sec) = explode(" ", microtime());
return ((float)$usec + (float)$sec);
}
/**
* dump a var.
*
* @param mixed $var
* @access public
* @return void
*/
function a($var)
{
echo "<xmp class='a-left'>";
print_r($var);
echo "</xmp>";
}
/**
* When the $var has the $key, return it, esle result one default value.
*
* @param array|object $var
* @param string|int $key
* @param mixed $valueWhenNone value when the key not exits.
* @param mixed $valueWhenExists value when the key exits.
* @access public
* @return void
*/
function zget($var, $key, $valueWhenNone = '', $valueWhenExists = '')
{
$var = (array)$var;
if(isset($var[$key]))
{
if($valueWhenExists) return $valueWhenExists;
return $var[$key];
}
return $valueWhenNone;
}
/**
* Judge the server ip is local or not.
*
* @access public
* @return void
*/
function isLocalIP()
{
$serverIP = $_SERVER['SERVER_ADDR'];
if($serverIP == '127.0.0.1') return true;
return !filter_var($serverIP, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE);
}
/**
* Get web root.
*
* @access public
* @return string
*/
function getWebRoot()
{
$path = $_SERVER['SCRIPT_NAME'];
if(PHP_SAPI == 'cli')
{
$url = parse_url($_SERVER['argv'][1]);
$path = empty($url['path']) ? '/' : rtrim($url['path'], '/');
$path = empty($path) ? '/' : preg_replace('/\/www$/', '/www/', $path);
}
return substr($path, 0, (strrpos($path, '/') + 1));
}
/**
* Check exist onlybody param.
*
* @access public
* @return void
*/
function isonlybody()
{
return (isset($_GET['onlybody']) and $_GET['onlybody'] == 'yes');
}

223
framework/model.class.php Executable file → Normal file
View File

@@ -1,5 +1,6 @@
<?php
/**
* ZenTaoPHP的model类。
* The model class file of ZenTaoPHP framework.
*
* The author disclaims copyright to this source code. In place of
@@ -11,226 +12,16 @@
*/
/**
* model基类。
* The base class of model.
*
* @package framework
*/
class model
include dirname(__FILE__) . '/base/model.class.php';
class model extends baseModel
{
/**
* The global $app object.
*
* @var object
* @access protected
*/
protected $app;
/**
* The global $config object.
*
* @var object
* @access protected
*/
protected $config;
/**
* The global $lang object.
*
* @var object
* @access protected
*/
protected $lang;
/**
* The global $dbh object, the database connection handler.
*
* @var object
* @access protected
*/
protected $dbh;
/**
* The $dao object, used to access or update database.
*
* @var object
* @access protected
*/
public $dao;
/**
* The $post object, used to access the $_POST var.
*
* @var ojbect
* @access public
*/
public $post;
/**
* The $get object, used to access the $_GET var.
*
* @var ojbect
* @access public
*/
public $get;
/**
* The $sesion object, used to access the $_SESSION var.
*
* @var ojbect
* @access public
*/
public $session;
/**
* The $server object, used to access the $_SERVER var.
*
* @var ojbect
* @access public
*/
public $server;
/**
* The $cookie object, used to access the $_COOKIE var.
*
* @var ojbect
* @access public
*/
public $cookie;
/**
* The $global object, used to access the $_GLOBAL var.
*
* @var ojbect
* @access public
*/
public $global;
/**
* The construct function.
*
* 1. global the global vars, refer them by the class member such as $this->app.
* 2. set the pathes, config, lang of current module
*
* @access public
* @return void
*/
public function __construct()
{
global $app, $config, $lang, $dbh;
$this->app = $app;
$this->config = $config;
$this->lang = $lang;
$this->dbh = $dbh;
$moduleName = $this->getModuleName();
$this->app->loadLang($moduleName, $exit = false);
$this->app->loadConfig($moduleName, $exit = false);
$this->loadDAO();
$this->setSuperVars();
}
/**
* Get the module name of this model. Not the module user visiting.
*
* This method replace the 'ext' and 'model' string from the model class name, thus get the module name.
* Not useing $app->getModuleName() because it return the module user is visiting. But one module can be
* loaded by loadModel() so we must get the module name of thie model.
*
* @access protected
* @return string the module name.
*/
protected function getModuleName()
{
$parentClass = get_parent_class($this);
$selfClass = get_class($this);
$className = $parentClass == 'model' ? $selfClass : $parentClass;
if($className == 'extensionModel') return 'extension';
return strtolower(str_ireplace(array('ext', 'Model'), '', $className));
}
/**
* Set the super vars.
*
* @access protected
* @return void
*/
protected function setSuperVars()
{
$this->post = $this->app->post;
$this->get = $this->app->get;
$this->server = $this->app->server;
$this->cookie = $this->app->cookie;
$this->session = $this->app->session;
$this->global = $this->app->global;
}
/**
* Load the model of one module. After loaded, can use $this->modulename to visit the model object.
*
* @param string $moduleName
* @access public
* @return object|bool the model object or false if model file not exists.
*/
public function loadModel($moduleName)
{
if(empty($moduleName)) return false;
$modelFile = helper::setModelFile($moduleName);
if(!helper::import($modelFile)) return false;
$modelClass = class_exists('ext' . $moduleName. 'model') ? 'ext' . $moduleName . 'model' : $moduleName . 'model';
if(!class_exists($modelClass)) $this->app->triggerError(" The model $modelClass not found", __FILE__, __LINE__, $exit = true);
$this->$moduleName = new $modelClass();
return $this->$moduleName;
}
/**
* Load extension class of a model. Saved to $moduleName/ext/model/class/$extensionName.class.php.
*
* @param string $extensionName
* @param string $moduleName
* @access public
* @return void
*/
public function loadExtension($extensionName, $moduleName = '')
{
if(empty($extensionName)) return false;
/* Set extenson name and extension file. */
$extensionName = strtolower($extensionName);
$moduleName = $moduleName ? $moduleName : $this->getModuleName();
$extensionFile = $this->app->getModuleExtPath($moduleName, 'model') . 'class/' . $extensionName . '.class.php';
/* Try to import parent model file auto and then import the extension file. */
if(!class_exists($moduleName . 'Model')) helper::import($this->app->getModulePath($moduleName) . 'model.php');
if(!helper::import($extensionFile)) return false;
/* Set the extension class name. */
$extensionClass = $extensionName . ucfirst($moduleName);
if(!class_exists($extensionClass)) return false;
/* Create an instance of the extension class and return it. */
$extensionObject = new $extensionClass;
$extensionClass = str_replace('Model', '', $extensionClass);
$this->$extensionClass = $extensionObject;
return $extensionObject;
}
//-------------------- DAO related method s--------------------//
/**
* Load DAO.
*
* @access private
* @return void
*/
private function loadDAO()
{
$this->dao = $this->app->loadClass('dao');
}
/**
* 删除记录
* Delete one record.
*
* @param string $table the table name
@@ -241,7 +32,7 @@ class model
public function delete($table, $id)
{
$this->dao->update($table)->set('deleted')->eq(1)->where('id')->eq($id)->exec();
$object = str_replace($this->config->db->prefix, '', $table);
$object = ltrim(strstr(trim($table, '`'), '_'), '_');
$this->loadModel('action')->create($object, $id, 'deleted', '', $extra = ACTIONMODEL::CAN_UNDELETED);
}
}
}

File diff suppressed because it is too large Load Diff

2007
lib/base/dao/dao.class.php Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,952 @@
<?php
/**
* ZenTaoPHP的验证和过滤类。
* The validater and fixer class file of ZenTaoPHP framework.
*
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
/**
* validater类检查数据是否符合规则。
* The validater class, checking data by rules.
*
* @package framework
*/
class baseValidater
{
/**
* 最大参数个数。
* The max count of args.
*/
const MAX_ARGS = 3;
/**
* 是否是Bool类型。
* Bool checking.
*
* @param bool $var
* @static
* @access public
* @return bool
*/
public static function checkBool($var)
{
return filter_var($var, FILTER_VALIDATE_BOOLEAN);
}
/**
* 是否是Int类型。
* Int checking.
*
* @param int $var
* @static
* @access public
* @return bool
*/
public static function checkInt($var)
{
$args = func_get_args();
if($var != 0) $var = ltrim($var, 0); // 去掉变量左边的000不是Int类型
// Remove the left 0, filter don't think 00 is an int.
/* 如果设置了最小的整数。 Min is setted. */
if(isset($args[1]))
{
/* 如果最大的整数也设置了。 And Max is setted. */
if(isset($args[2]))
{
$options = array('options' => array('min_range' => $args[1], 'max_range' => $args[2]));
}
else
{
$options = array('options' => array('min_range' => $args[1]));
}
return filter_var($var, FILTER_VALIDATE_INT, $options);
}
else
{
return filter_var($var, FILTER_VALIDATE_INT);
}
}
/**
* 检查不是Int类型。
* Not int checking.
*
* @param int $var
* @static
* @access public
* @return bool
*/
public static function checkNotInt($var)
{
return !self::checkInt($var);
}
/**
* 检查Float类型。
* Float checking.
*
* @param float $var
* @param string $decimal
* @static
* @access public
* @return bool
*/
public static function checkFloat($var, $decimal = '.')
{
return filter_var($var, FILTER_VALIDATE_FLOAT, array('options' => array('decimal' => $decimal)));
}
/**
* 检查Email。
* Email checking.
*
* @param string $var
* @static
* @access public
* @return bool
*/
public static function checkEmail($var)
{
return filter_var($var, FILTER_VALIDATE_EMAIL);
}
/**
* 检查电话或手机号码
* Check phone number.
*
* @param string $var
* @static
* @access public
* @return void
*/
public static function checkPhone($var)
{
return (validater::checkTel($var) or validater::checkMobile($var));
}
/**
* 检查电话号码
* Check tel number.
*
* @param int $var
* @static
* @access public
* @return void
*/
public static function checkTel($var)
{
return preg_match("/^([0-9]{3,4}-)?[0-9]{7,8}$/", $var);
}
/**
* 检查手机号码
* Check mobile number.
*
* @param string $var
* @static
* @access public
* @return void
*/
public static function checkMobile($var)
{
return preg_match("/^1[3-5,8]{1}[0-9]{9}$/", $var);
}
/**
* 检查网址。
* 该规则不支持中文字符的网址。
*
* URL checking.
* The check rule of filter don't support chinese.
*
* @param string $var
* @static
* @access public
* @return bool
*/
public static function checkURL($var)
{
return filter_var($var, FILTER_VALIDATE_URL);
}
/**
* 检查域名,不支持中文。
* Domain checking.
*
* The check rule of filter don't support chinese.
*
* @param string $var
* @static
* @access public
* @return bool
*/
public static function checkDomain($var)
{
return preg_match('/^([a-z0-9-]+\.)+[a-z]{2,15}$/', $var);
}
/**
* 检查IP地址。
* IP checking.
*
* @param ip $var
* @param string $range all|public|static|private
* @static
* @access public
* @return bool
*/
public static function checkIP($var, $range = 'all')
{
if($range == 'all') return filter_var($var, FILTER_VALIDATE_IP);
if($range == 'public static') return filter_var($var, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE);
if($range == 'private')
{
if($var == '127.0.0.1' or filter_var($var, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false) return true;
return false;
}
}
/**
* 日期检查。注意2009-09-31是一个合法日期系统会将它转换为2009-10-01。
* Date checking. Note: 2009-09-31 will be an valid date, because strtotime auto fixed it to 10-01.
*
* @param date $date
* @static
* @access public
* @return bool
*/
public static function checkDate($date)
{
if($date == '0000-00-00') return true;
$stamp = strtotime($date);
if(!is_numeric($stamp)) return false;
return checkdate(date('m', $stamp), date('d', $stamp), date('Y', $stamp));
}
/**
* 检查正则表达式。
* REG checking.
*
* @param string $var
* @param string $reg
* @static
* @access public
* @return bool
*/
public static function checkREG($var, $reg)
{
return filter_var($var, FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => $reg)));
}
/**
* 检查长度。
* Length checking.
*
* @param string $var
* @param string $max
* @param int $min
* @static
* @access public
* @return bool
*/
public static function checkLength($var, $max, $min = 0)
{
$length = function_exists('mb_strlen') ? mb_strlen($var, 'utf-8') : strlen($var);
return self::checkInt($length, $min, $max);
}
/**
* 检查不为空。
* Not empty checking.
*
* @param mixed $var
* @static
* @access public
* @return bool
*/
public static function checkNotEmpty($var)
{
return !empty($var);
}
/**
* 检查为空。
* Empty checking.
*
* @param mixed $var
* @static
* @access public
* @return bool
*/
public static function checkEmpty($var)
{
return empty($var);
}
/**
* 检查用户名。
* Account checking.
*
* @param string $var
* @static
* @access public
* @return bool
*/
public static function checkAccount($var)
{
global $config;
$accountRule = empty($config->accountRule) ? '|^[a-zA-Z0-9_]{1}[a-zA-Z0-9_\.]{1,}[a-zA-Z0-9_]{1}$|' : $config->accountRule;
return self::checkREG($var, $accountRule);
}
/**
* 检查Code。
* Check code.
*
* @param string $var
* @static
* @access public
* @return bool
*/
public static function checkCode($var)
{
return self::checkREG($var, '|^[A-Za-z0-9]+$|');
}
/**
* 检查验证码。
* Check captcha.
*
* @param mixed $var
* @static
* @access public
* @return bool
*/
public static function checkCaptcha($var)
{
if(!isset($_SESSION['captcha'])) return false;
return $var == $_SESSION['captcha'];
}
/**
* 是否等于给定的值。
* Must equal a value.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkEqual($var, $value)
{
return $var == $value;
}
/**
* 检查不等于给定的值
* Must not equal a value.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkNotEqual($var, $value)
{
return $var != $value;
}
/**
* 检查大于给定的值。
* Must greater than a value.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkGT($var, $value)
{
return $var > $value;
}
/**
* 检查小于给定的值
* Must less than a value.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkLT($var, $value)
{
return $var < $value;
}
/**
* 检查大于等于给定的值
* Must greater than a value or equal a value.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkGE($var, $value)
{
return $var >= $value;
}
/**
* 检查小于等于给定的值
* Must less than a value or equal a value.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkLE($var, $value)
{
return $var <= $value;
}
/**
* 检查是否在给定的列表里面。
* Must in value list.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkIn($var, $value)
{
if(!is_array($value)) $value = explode(',', $value);
return in_array($var, $value);
}
/**
* 检查文件名。
* Check file name.
*
* @param string $var
* @static
* @access public
* @return bool
*/
public static function checkFileName($var)
{
return !preg_match('/>+|:+|<+/', $var);
}
/**
* 检查敏感词。
* Check sensitive words.
*
* @param object $vars
* @param array $dicts
* @static
* @access public
* @return void
*/
public static function checkSensitive($vars, $dicts)
{
foreach($vars as $var)
{
if(!$var) continue;
foreach($dicts as $dict)
{
if(strpos($var, $dict) === false) continue;
if(strpos($var, $dict) !== false) return false;
}
}
return true;
}
/**
* 调用一个方法进行检查。
* Call a function to check it.
*
* @param mixed $var
* @param string $func
* @static
* @access public
* @return bool
*/
public static function call($var, $func)
{
return filter_var($var, FILTER_CALLBACK, array('options' => $func));
}
}
/**
* fixer类处理数据。
* fixer class, to fix data types.
*
* @package framework
*/
class baseFixer
{
/**
* 处理的数据。
* The data to be fixed.
*
* @var ojbect
* @access public
*/
public $data;
/**
* 跳过处理的字段。
* The fields to striped.
*
* @var array
* @access public
*/
public $stripedFields = array();
/**
* 构造方法,将超级全局变量转换为对象。
* The construction function, according the scope, convert it to object.
*
* @param string $scope the scope of the var, should be post|get|server|session|cookie|env
* @access public
* @return void
*/
public function __construct($scope)
{
switch($scope)
{
case 'post':
$this->data = (object)$_POST;
break;
case 'server':
$this->data = (object)$_SERVER;
break;
case 'get':
$this->data = (object)$_GET;
break;
case 'session':
$this->data = (object)$_SESSION;
break;
case 'cookie':
$this->data = (object)$_COOKIE;
break;
case 'env':
$this->data = (object)$_ENV;
break;
case 'file':
$this->data = (object)$_FILES;
break;
default:
die('scope not supported, should be post|get|server|session|cookie|env');
}
}
/**
* 工厂方法。
* The factory function.
*
* @param string $scope
* @access public
* @return object fixer object.
*/
public static function input($scope)
{
return new fixer($scope);
}
/**
* 处理Email。
* Email fix.
*
* @param string $fieldName
* @access public
* @return object fixer object.
*/
public function cleanEmail($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_EMAIL);
return $this;
}
/**
* url编码。
* urlencode.
*
* @param string $fieldName
* @access public
* @return object fixer object.
*/
public function encodeURL($fieldName)
{
$fields = $this->processFields($fieldName);
$args = func_get_args();
foreach($fields as $fieldName)
{
$this->data->$fieldName = isset($args[1]) ? filter_var($this->data->$fieldName, FILTER_SANITIZE_ENCODED, $args[1]) : filter_var($this->data->$fieldName, FILTER_SANITIZE_ENCODED);
}
return $this;
}
/**
* 清理网址。
* Clean the url.
*
* @param string $fieldName
* @access public
* @return object fixer object.
*/
public function cleanURL($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_URL);
return $this;
}
/**
* 处理Float类型。
* Float fixer.
*
* @param string $fieldName
* @access public
* @return object fixer object.
*/
public function cleanFloat($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION|FILTER_FLAG_ALLOW_THOUSAND);
return $this;
}
/**
* 处理Int类型。
* Int fixer.
*
* @param string $fieldName
* @access public
* @return object fixer object.
*/
public function cleanINT($fieldName = '')
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_NUMBER_INT);
return $this;
}
/**
* 将字符串转换为可以在浏览器查看的编码。
* Special chars.
*
* @param string $fieldName
* @access public
* @return object fixer object
*/
public function specialChars($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName)
{
if(empty($this->stripedFields) or !in_array($fieldName, $this->stripedFields)) $this->data->$fieldName = $this->specialArray($this->data->$fieldName);
}
return $this;
}
/**
* Special array
*
* @param mix $data
* @access public
* @return mix
*/
public function specialArray($data)
{
if(!is_array($data)) return htmlspecialchars($data, ENT_QUOTES);
foreach($data as &$value) $value = $this->specialArray($value);
return $data;
}
/**
* 忽略该标签。
* Strip tags
*
* @param string $fieldName
* @param string $allowableTags
* @access public
* @return object fixer object
*/
public function stripTags($fieldName, $allowedTags = '')
{
global $app, $config;
if(empty($allowedTags) and isset($config->allowedTags)) $allowedTags = $config->allowedTags;
$usePurifier = isset($config->framework->purifier) ? $config->framework->purifier : false;
if($usePurifier)
{
$app->loadClass('purifier', true);
$purifierConfig = HTMLPurifier_Config::createDefault();
$purifierConfig->set('Filter.YouTube', 1);
/* Disable caching. */
$purifierConfig->set('Cache.DefinitionImpl', null);
$purifier = new HTMLPurifier($purifierConfig);
$def = $purifierConfig->getHTMLDefinition(true);
$def->addAttribute('a', 'target', 'Enum#_blank,_self,_target,_top');
}
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName)
{
if(version_compare(phpversion(), '5.4', '<') and get_magic_quotes_gpc()) $this->data->$fieldName = stripslashes($this->data->$fieldName);
if(!in_array($fieldName, $this->stripedFields))
{
if(!defined('RUN_MODE') or RUN_MODE != 'admin')
{
/*
* purifier会把&nbsp;替换空格kindeditor在会吧行首的空格去掉。
* purifier will change &nbsp; to ' ', and edit it will no space in line head use kindeditor.
**/
if($usePurifier) $this->data->$fieldName = str_replace('&nbsp;', '&spnb;', $this->data->$fieldName);
$this->data->$fieldName = $usePurifier ? $purifier->purify($this->data->$fieldName) : strip_tags($this->data->$fieldName, $allowedTags);
if($usePurifier) $this->data->$fieldName = str_replace('&amp;spnb;', '&nbsp;', $this->data->$fieldName);
}
}
$this->stripedFields[] = $fieldName;
}
return $this;
}
/**
* 忽略处理给定的字段。
* Skip special chars check.
*
* @param string $filename
* @access public
* @return object fixer object
*/
public function skipSpecial($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->stripedFields[] = $fieldName;
return $this;
}
/**
* 给字段添加引用,防止字符与关键字冲突。
* Quote
*
* @param string $fieldName
* @access public
* @return object fixer object
*/
public function quote($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_MAGIC_QUOTES);
return $this;
}
/**
* 设置字段的默认值。
* Set default value of some fileds.
*
* @param string $fields
* @param mixed $value
* @access public
* @return object fixer object
*/
public function setDefault($fields, $value)
{
$fields = strpos($fields, ',') ? explode(',', str_replace(' ', '', $fields)) : array($fields);
foreach($fields as $fieldName)if(!isset($this->data->$fieldName) or empty($this->data->$fieldName)) $this->data->$fieldName = $value;
return $this;
}
/**
* 如果条件为真,则为字段赋值。
* Set value of a filed on the condition is true.
*
* @param bool $condition
* @param string $fieldName
* @param string $value
* @access public
* @return object fixer object
*/
public function setIF($condition, $fieldName, $value)
{
if($condition) $this->data->$fieldName = $value;
return $this;
}
/**
* 强制给字段赋值。
* Set the value of a filed in force.
*
* @param string $fieldName
* @param mixed $value
* @access public
* @return object fixer object
*/
public function setForce($fieldName, $value)
{
$this->data->$fieldName = $value;
return $this;
}
/**
* 移除一个字段。
* Remove a field.
*
* @param string $fieldName
* @access public
* @return object fixer object
*/
public function remove($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) unset($this->data->$fieldName);
return $this;
}
/**
* 如果条件为真,移除该字段。
* Remove a filed on the condition is true.
*
* @param bool $condition
* @param string $fields
* @access public
* @return object fixer object
*/
public function removeIF($condition, $fields)
{
$fields = $this->processFields($fields);
if($condition) foreach($fields as $fieldName) unset($this->data->$fieldName);
return $this;
}
/**
* 为数据添加新的项。
* Add an item to the data.
*
* @param string $fieldName
* @param mixed $value
* @access public
* @return object fixer object
*/
public function add($fieldName, $value)
{
$this->data->$fieldName = $value;
return $this;
}
/**
* 如果条件为真,则为数据添加新的项。
* Add an item to the data on the condition if true.
*
* @param bool $condition
* @param string $fieldName
* @param mixed $value
* @access public
* @return object fixer object
*/
public function addIF($condition, $fieldName, $value)
{
if($condition) $this->data->$fieldName = $value;
return $this;
}
/**
* 为指定字段增加值。
* Join the field.
*
* @param string $fieldName
* @param string $value
* @access public
* @return object fixer object
*/
public function join($fieldName, $value)
{
if(!isset($this->data->$fieldName) or !is_array($this->data->$fieldName)) return $this;
$this->data->$fieldName = join($value, $this->data->$fieldName);
return $this;
}
/**
* 调用一个方法来处理数据。
* Call a function to fix it.
*
* @param string $fieldName
* @param string $func
* @access public
* @return object fixer object
*/
public function callFunc($fieldName, $func)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_CALLBACK, array('options' => $func));
return $this;
}
/**
* 处理完成后返回数据。
* Get the data after fixing.
*
* @param string $fieldName
* @access public
* @return object
*/
public function get($fields = '')
{
$fields = str_replace(' ', '', trim($fields));
foreach($this->data as $field => $value) $this->specialChars($field);
if(empty($fields)) return $this->data;
if(strpos($fields, ',') === false) return $this->data->$fields;
$fields = array_flip(explode(',', $fields));
foreach($this->data as $field => $value)
{
if(!isset($fields[$field])) unset($this->data->$field);
if(!in_array($field, $this->stripedFields)) $this->data->$field = $this->specialChars($this->data->field);
}
return $this->data;
}
/**
* 处理字段,如果字段中含有',',拆分成数组。如果字段不在$data中删除掉。
* Process fields, if contains ',', split it to array. If not in $data, remove it.
*
* @param string $fields
* @access public
* @return array
*/
public function processFields($fields)
{
$fields = strpos($fields, ',') ? explode(',', str_replace(' ', '', $fields)) : array($fields);
foreach($fields as $key => $fieldName) if(!isset($this->data->$fieldName)) unset($fields[$key]);
return $fields;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,620 @@
<?php
/**
* ZenTaoPHP的分页类。
* The pager class file of ZenTaoPHP framework.
*
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
/**
* pager类.
* Pager class.
*
* @package framework
*/
class basePager
{
/**
* 每页的默认显示记录数。
* The default counts of per page.
*
* @public int
*/
const DEFAULT_REC_PER_PAGE = 20;
/**
* 总个数。
* The total counts.
*
* @var int
* @access public
*/
public $recTotal;
/**
* 每页的记录数。
* Record count per page.
*
* @var int
* @access public
*/
public $recPerPage;
/**
* The cookie name of recPerPage.
*
* @var string
* @access public
*/
public $pageCookie;
/**
* 总页面数。
* Page count.
*
* @var string
* @access public
*/
public $pageTotal;
/**
* 当前页码。
* Current page id.
*
* @var string
* @access public
*/
public $pageID;
/**
* 全局变量$app。
* The global $app.
*
* @var object
* @access public
*/
public $app;
/**
* 全局变量$lang。
* The global $lang.
*
* @var object
* @access public
*/
public $lang;
/**
* 当前的模块名。
* Current module name.
*
* @var string
* @access public
*/
public $moduleName;
/**
* 当前的方法名。
* Current method.
*
* @var string
* @access public
*/
public $methodName;
/**
* 参数信息。
* The params.
*
* @public array
*/
public $params;
/**
* 构造方法。
* The construct function.
*
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function __construct($recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->setApp();
$this->setLang();
$this->setModuleName();
$this->setMethodName();
$this->setRecTotal($recTotal);
$this->setRecPerPage($recPerPage);
$this->setPageTotal();
$this->setPageID($pageID);
}
/**
* 构造方法。
* The factory function.
*
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return object
*/
public static function init($recTotal = 0, $recPerPage = 20, $pageID = 1)
{
return new pager($recTotal, $recPerPage, $pageID);
}
/**
* 设置总记录数。
* Set the recTotal property.
*
* @param int $recTotal
* @access public
* @return void
*/
public function setRecTotal($recTotal = 0)
{
$this->recTotal = (int)$recTotal;
}
/**
* 设置每页记录数。
* Set the recPerPage property.
*
* @param int $recPerPage
* @access public
* @return void
*/
public function setRecPerPage($recPerPage)
{
/* Set the cookie name. */
$this->pageCookie = 'pager' . ucfirst($this->app->getModuleName()) . ucfirst($this->app->getMethodName());
if(isset($_COOKIE[$this->pageCookie])) $recPerPage = $_COOKIE[$this->pageCookie];
$this->recPerPage = ($recPerPage > 0) ? $recPerPage : PAGER::DEFAULT_REC_PER_PAGE;
}
/**
* 设置总页数。
* Set the pageTotal property.
*
* @access public
* @return void
*/
public function setPageTotal()
{
$this->pageTotal = ceil($this->recTotal / $this->recPerPage);
}
/**
* 设置页码。
* Set the page id.
*
* @param int $pageID
* @access public
* @return void
*/
public function setPageID($pageID)
{
if($pageID > 0 and ($this->pageTotal == 0 or $pageID <= $this->pageTotal))
{
$this->pageID = $pageID;
}
else
{
$this->pageID = 1;
}
}
/**
* 设置全局变量$app。
* Set the $app property;
*
* @access public
* @return void
*/
public function setApp()
{
global $app;
$this->app = $app;
}
/**
* 设置全局变量$lang。
* Set the $lang property.
*
* @access public
* @return void
*/
public function setLang()
{
global $lang;
$this->lang = $lang;
}
/**
* 设置模块名。
* Set the $moduleName property.
*
* @access public
* @return void
*/
public function setModuleName()
{
$this->moduleName = $this->app->getModuleName();
}
/**
* 设置方法名。
* Set the $methodName property.
*
* @access public
* @return void
*/
public function setMethodName()
{
$this->methodName = $this->app->getMethodName();
}
/**
* 从请求网址中获取记录总数、每页记录数、页码。
* Get recTotal, recPerpage, pageID from the request params, and add them to params.
*
* @access public
* @return void
*/
public function setParams()
{
$this->params = $this->app->getParams();
foreach($this->params as $key => $value)
{
if(strtolower($key) == 'rectotal') $this->params[$key] = $this->recTotal;
if(strtolower($key) == 'recperpage') $this->params[$key] = $this->recPerPage;
if(strtolower($key) == 'pageid') $this->params[$key] = $this->pageID;
}
parse_str(strip_tags(urldecode($_SERVER['QUERY_STRING'])), $query);
unset($query['m']);
unset($query['f']);
unset($query['t']);
$this->params = array_merge($this->params, $query);
}
/**
* 创建limit语句。
* Create the limit string.
*
* @access public
* @return string
*/
public function limit()
{
$limit = '';
if($this->pageTotal > 1) $limit = ' limit ' . ($this->pageID - 1) * $this->recPerPage . ", $this->recPerPage";
return $limit;
}
/**
* 向页面显示分页信息。
* Print the pager's html.
*
* @param string $align
* @param string $type
* @access public
* @return void
*/
public function show($align = 'right', $type = 'full')
{
if($align === 'justify')
{
echo $this->getJustify($type);
}
else
{
echo $this->get($align, $type);
}
}
/**
* 获取优化后的分页。
* Get the justify pager html string
*
* @access public
* @return [type] [description]
*/
public function getJustify()
{
if($this->recTotal <= 0) return '';
$this->setParams();
$pager = '';
$pager .= "<li class='previous" . ($this->pageID == 1 ? ' disabled' : '') . "'>";
$this->params['pageID'] = 1;
$pager .= $this->createLink('« ' . $this->lang->pager->previousPage) . '</li>';
$pager .= "<li class='caption'>";
$firstId = $this->recPerPage * ($this->pageID - 1) + 1;
$pager .= sprintf($this->lang->pager->summery, $firstId, max(min($this->recPerPage * $this->pageID, $this->recTotal), $firstId), $this->recTotal);
$pager .= '</li>';
$pager .= "<li class='next" . (($this->pageID == $this->pageTotal || $this->pageTotal <= 1) ? ' disabled' : '') . "'>";
$this->params['pageID'] = min($this->pageTotal, $this->pageID + 1);
$pager .= $this->createLink($this->lang->pager->nextPage . ' »') . '</li>';
return "<ul class='pager pager-justify'>{$pager}</ul>";
}
/**
* 设置分页信息的样式。
* Get the pager html string.
*
* @param string $align
* @param string $type the pager type, full|short|shortest
* @access public
* @return string
*/
public function get($align = 'right', $type = 'full')
{
/* 如果记录个数为0返回没有记录。 */
/* If the RecTotal is zero, return with no record. */
if($this->recTotal == 0) return $type == 'mobile' ? '' : "<div style='float:$align; clear:none;' class='page'>{$this->lang->pager->noRecord}</div>";
/* Set the params. */
$this->setParams();
/* 创建前一页和后一页链接。 */
/* Create the prePage and nextpage, all types have them. */
$pager = $this->createPrePage($type);
$pager .= $this->createNextPage($type);
/* 简单和完全模式。 The short and full type. */
if($type !== 'shortest' and $type !== 'mobile')
{
$pager = $this->createFirstPage() . $pager;
$pager .= $this->createLastPage();
}
if($type == 'mobile')
{
$position = $this->pageTotal == 1 ? '' : $this->pageID . '/' . $this->pageTotal;
$pager = $pager . ' ' . $position;
}
else if($type != 'full')
{
$pager = $this->pageID . '/' . $this->pageTotal . ' ' . $pager;
}
/* 只是完全模式。 Only the full type . */
if($type == 'full')
{
$pager = $this->createDigest() . $pager;
$pager .= $this->createGoTo();
$pager .= $this->createRecPerPageJS();
}
return "<div style='float:$align; clear:none;' class='pager form-inline'>$pager</div>";
}
/**
* 生成分页摘要信息。
* Create the digest code.
*
* @access public
* @return string
*/
public function createDigest()
{
return sprintf($this->lang->pager->digest, $this->recTotal, $this->createRecPerPageList(), $this->pageID, $this->pageTotal);
}
/**
* 创建首页链接。
* Create the first page.
*
* @access public
* @return string
*/
public function createFirstPage()
{
if($this->pageID == 1) return $this->lang->pager->first . ' ';
$this->params['pageID'] = 1;
return $this->createLink($this->lang->pager->first);
}
/**
* 创建前一页链接。
* Create the pre page html.
*
* @param string $type
* @access public
* @return string
*/
public function createPrePage($type = 'full')
{
if($type == 'mobile')
{
if($this->pageID == 1) return '';
$this->params['pageID'] = $this->pageID - 1;
return $this->createLink($this->lang->pager->pre);
}
else
{
if($this->pageID == 1) return $this->lang->pager->pre . ' ';
$this->params['pageID'] = $this->pageID - 1;
return $this->createLink($this->lang->pager->pre);
}
}
/**
* 创建下一页链接。
* Create the next page html.
*
* @param string $type
* @access public
* @return string
*/
public function createNextPage($type = 'full')
{
if($type == 'mobile')
{
if($this->pageID == $this->pageTotal) return '';
$this->params['pageID'] = $this->pageID + 1;
return $this->createLink($this->lang->pager->next);
}
else
{
if($this->pageID == $this->pageTotal) return $this->lang->pager->next . ' ';
$this->params['pageID'] = $this->pageID + 1;
return $this->createLink($this->lang->pager->next);
}
}
/**
* 创建最后一页链接。
* Create the last page
*
* @access public
* @return string
*/
public function createLastPage()
{
if($this->pageID == $this->pageTotal) return $this->lang->pager->last . ' ';
$this->params['pageID'] = $this->pageTotal;
return $this->createLink($this->lang->pager->last);
}
/**
* 创建每页显示记录数的select标签。
* Create the select object of record perpage.
*
* @access public
* @return string
*/
public function createRecPerPageJS()
{
/*
* 替换recTotal, recPerPage, pageID为特殊的字符串然后用js代码替换掉。
* Replace the recTotal, recPerPage, pageID to special string, and then replace them with values by JS.
**/
$params = $this->params;
foreach($params as $key => $value)
{
if(strtolower($key) == 'rectotal') $params[$key] = '_recTotal_';
if(strtolower($key) == 'recperpage') $params[$key] = '_recPerPage_';
if(strtolower($key) == 'pageid') $params[$key] = '_pageID_';
}
$vars = '';
foreach($params as $key => $value) $vars .= "$key=$value&";
$vars = rtrim($vars, '&');
$js = <<<EOT
<script language='Javascript'>
vars = '$vars';
pageCookie = '$this->pageCookie';
function submitPage(mode, perPage)
{
pageTotal = parseInt(document.getElementById('_pageTotal').value);
pageID = document.getElementById('_pageID').value;
recPerPage = document.getElementById('_recPerPage').getAttribute('data-value');
recTotal = document.getElementById('_recTotal').value;
if(mode == 'changePageID')
{
if(pageID > pageTotal) pageID = pageTotal;
if(pageID < 1) pageID = 1;
}
else if(mode == 'changeRecPerPage')
{
recPerPage = perPage;
pageID = 1;
}
$.cookie(pageCookie, recPerPage, {expires:config.cookieLife, path:config.webRoot});
vars = vars.replace('_recTotal_', recTotal)
vars = vars.replace('_recPerPage_', recPerPage)
vars = vars.replace('_pageID_', pageID);
location.href=createLink('$this->moduleName', '$this->methodName', vars);
}
</script>
EOT;
return $js;
}
/**
* 生成每页显示记录数的select列表。
* Create the select list of RecPerPage.
*
* @access public
* @return string
*/
public function createRecPerPageList()
{
for($i = 5; $i <= 50; $i += 5) $range[$i] = $i;
$range[100] = 100;
$range[200] = 200;
$range[500] = 500;
$range[1000] = 1000;
$range[2000] = 2000;
$html = "<div class='dropdown dropup'><a href='javascript:;' data-toggle='dropdown' id='_recPerPage' data-value='{$this->recPerPage}'>" . (sprintf($this->lang->pager->recPerPage, $this->recPerPage)) . "<span class='caret'></span></a><ul class='dropdown-menu'>";
foreach ($range as $key => $value)
{
$html .= '<li' . ($this->recPerPage == $value ? " class='active'" : '') .'>' . "<a href='javascript:submitPage(\"changeRecPerPage\", $value)'>{$value}</a>" . '</li>';
}
$html .= '</ul></div>';
return $html;
}
/**
* 生成跳转到指定页码的部分。
* Create the goto part html.
*
* @access public
* @return string
*/
public function createGoTo()
{
$goToHtml = "<input type='hidden' id='_recTotal' value='$this->recTotal' />\n";
$goToHtml .= "<input type='hidden' id='_pageTotal' value='$this->pageTotal' />\n";
$goToHtml .= "<input type='text' id='_pageID' value='$this->pageID' style='text-align:center;width:30px;' class='form-control' /> \n";
$goToHtml .= "<input type='button' id='goto' value='{$this->lang->pager->locate}' onclick='submitPage(\"changePageID\");' class='btn'/>";
return $goToHtml;
}
/**
* 创建链接。
* Create link.
*
* @param string $title
* @access public
* @return string
*/
public function createLink($title)
{
global $config;
if(helper::inSeoMode() && method_exists('uri', 'create' . $this->moduleName . $this->methodName))
{
$link = strip_tags(urldecode($_SERVER['REQUEST_URI']));
if($this->params['pageID'] == 1) return html::a(preg_replace('/\/p\d+\./', '.', $link), $title);
if(preg_match('/\/p\d+/', $link)) return html::a(preg_replace('/\/p\d+\./', '/p' . $this->params['pageID'] . '.', $link), $title);
if($config->requestType == 'PATH_INFO2') $link = str_replace('index.php/', 'index_php/', $link);
$link = str_replace('.', "/p{$this->params['pageID']}.", $link);
if($config->requestType == 'PATH_INFO2') $link = str_replace('index_php/', 'index.php/', $link);
return html::a($link, $title);
}
return html::a(helper::createLink($this->moduleName, $this->methodName, $this->params), $title);
}
}

1699
lib/dao/dao.class.php Executable file → Normal file

File diff suppressed because it is too large Load Diff

740
lib/filter/filter.class.php Executable file → Normal file
View File

@@ -1,755 +1,33 @@
<?php
/**
* ZenTaoPHP的验证和过滤类。
* The validater and fixer class file of ZenTaoPHP framework.
*
* The author disclaims copyright to this source code. In place of
* a legal notice, here is a blessing:
*
*
* May you do good and not evil.
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
helper::import(dirname(dirname(__FILE__)) . '/base/filter/filter.class.php');
/**
* The valida class, checking datas by rules.
*
* validater类检查数据是否符合规则。
* The validater class, checking data by rules.
*
* @package framework
*/
class validater
class validater extends baseValidater
{
/**
* The max count of args.
*/
const MAX_ARGS = 3;
/**
* Bool checking.
*
* @param bool $var
* @static
* @access public
* @return bool
*/
public static function checkBool($var)
{
return filter_var($var, FILTER_VALIDATE_BOOLEAN);
}
/**
* Int checking.
*
* @param int $var
* @static
* @access public
* @return bool
*/
public static function checkInt($var)
{
$args = func_get_args();
if($var != 0) $var = ltrim($var, 0); // Remove the left 0, filter don't think 00 is an int.
/* Min is setted. */
if(isset($args[1]))
{
/* And Max is setted. */
if(isset($args[2]))
{
$options = array('options' => array('min_range' => $args[1], 'max_range' => $args[2]));
}
else
{
$options = array('options' => array('min_range' => $args[1]));
}
return filter_var($var, FILTER_VALIDATE_INT, $options);
}
else
{
return filter_var($var, FILTER_VALIDATE_INT);
}
}
/**
* Float checking.
*
* @param float $var
* @param string $decimal
* @static
* @access public
* @return bool
*/
public static function checkFloat($var, $decimal = '.')
{
return filter_var($var, FILTER_VALIDATE_FLOAT, array('options' => array('decimail' => $decimal)));
}
/**
* Email checking.
*
* @param string $var
* @static
* @access public
* @return bool
*/
public static function checkEmail($var)
{
return filter_var($var, FILTER_VALIDATE_EMAIL);
}
/**
* URL checking.
*
* The check rule of filter don't support chinese.
*
* @param string $var
* @static
* @access public
* @return bool
*/
public static function checkURL($var)
{
return filter_var($var, FILTER_VALIDATE_URL);
}
/**
* IP checking.
*
* @param ip $var
* @param string $range all|public|static|private
* @static
* @access public
* @return bool
*/
public static function checkIP($var, $range = 'all')
{
if($range == 'all') return filter_var($var, FILTER_VALIDATE_IP);
if($range == 'public static') return filter_var($var, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE);
if($range == 'private')
{
if($var == '127.0.0.1' or filter_var($var, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE) === false) return true;
return false;
}
}
/**
* Date checking. Note: 2009-09-31 will be an valid date, because strtotime auto fixed it to 10-01.
*
* @param date $date
* @static
* @access public
* @return bool
*/
public static function checkDate($date)
{
if($date == '0000-00-00') return true;
$stamp = strtotime($date);
if(!is_numeric($stamp)) return false;
return checkdate(date('m', $stamp), date('d', $stamp), date('Y', $stamp));
}
/**
* REG checking.
*
* @param string $var
* @param string $reg
* @static
* @access public
* @return bool
*/
public static function checkREG($var, $reg)
{
return filter_var($var, FILTER_VALIDATE_REGEXP, array('options' => array('regexp' => $reg)));
}
/**
* Length checking.
*
* @param string $var
* @param string $max
* @param int $min
* @static
* @access public
* @return bool
*/
public static function checkLength($var, $max, $min = 0)
{
$length = function_exists('mb_strlen') ? mb_strlen($var, 'utf-8') : strlen($var);
return self::checkInt($length, $min, $max);
}
/**
* Not empty checking.
*
* @param mixed $var
* @static
* @access public
* @return bool
*/
public static function checkNotEmpty($var)
{
return !empty($var);
}
/**
* Empty checking.
*
* @param mixed $var
* @static
* @access public
* @return bool
*/
public static function checkEmpty($var)
{
return empty($var);
}
/**
* Account checking.
*
* @param string $var
* @static
* @access public
* @return bool
*/
public static function checkAccount($var)
{
global $config;
$accountRule = empty($config->accountRule) ? '|^[a-zA-Z0-9_]{1}[a-zA-Z0-9_\.]{1,}[a-zA-Z0-9_]{1}$|' : $config->accountRule;
return self::checkREG($var, $accountRule);
}
/**
* Check captcha.
*
* @param mixed $var
* @static
* @access public
* @return bool
*/
public static function checkCaptcha($var)
{
if(!isset($_SESSION['captcha'])) return false;
return $var == $_SESSION['captcha'];
}
/**
* Must equal a value.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkEqual($var, $value)
{
return $var == $value;
}
/**
* Must greater than a value.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkGT($var, $value)
{
return $var > $value;
}
/**
* Must less than a value.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkLT($var, $value)
{
return $var < $value;
}
/**
* Must greater than a value or equal a value.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkGE($var, $value)
{
return $var >= $value;
}
/**
* Must less than a value or equal a value.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkLE($var, $value)
{
return $var <= $value;
}
/**
* Must in value list.
*
* @param mixed $var
* @param mixed $value
* @static
* @access public
* @return bool
*/
public static function checkIn($var, $value)
{
if(!is_array($value)) $value = explode(',', $value);
return in_array($var, $value);
}
/**
* Check file name.
*
* @param string $var
* @static
* @access public
* @return bool
*/
public static function checkFileName($var)
{
return !preg_match('/>+|:+|<+/', $var);
}
/**
* Call a function to check it.
*
* @param mixed $var
* @param string $func
* @static
* @access public
* @return bool
*/
public static function call($var, $func)
{
return filter_var($var, FILTER_CALLBACK, array('options' => $func));
}
}
/**
* fixer类处理数据。
* fixer class, to fix data types.
*
* @package framework
*/
class fixer
class fixer extends baseFixer
{
/**
* The data to be fixed.
*
* @var ojbect
* @access private
*/
private $data;
private $stripedFields = array();
/**
* The construction function, according the scope, convert it to object.
*
* @param string $scope the scope of the var, should be post|get|server|session|cookie|env
* @access private
* @return void
*/
private function __construct($scope)
{
switch($scope)
{
case 'post':
$this->data = (object)$_POST;
break;
case 'server':
$this->data = (object)$_SERVER;
break;
case 'get':
$this->data = (object)$_GET;
break;
case 'session':
$this->data = (object)$_SESSION;
break;
case 'cookie':
$this->data = (object)$_COOKIE;
break;
case 'env':
$this->data = (object)$_ENV;
break;
case 'file':
$this->data = (object)$_FILES;
break;
default:
die('scope not supported, should be post|get|server|session|cookie|env');
}
}
/**
* The factory function.
*
* @param string $scope
* @access public
* @return object fixer object.
*/
public static function input($scope)
{
return new fixer($scope);
}
/**
* Email fix.
*
* @param string $fieldName
* @access public
* @return object fixer object.
*/
public function cleanEmail($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_EMAIL);
return $this;
}
/**
* urlenocde.
*
* @param string $fieldName
* @access public
* @return object fixer object.
*/
public function encodeURL($fieldName)
{
$fields = $this->processFields($fieldName);
$args = func_get_args();
foreach($fields as $fieldName)
{
$this->data->$fieldName = isset($args[1]) ? filter_var($this->data->$fieldName, FILTER_SANITIZE_ENCODED, $args[1]) : filter_var($this->data->$fieldName, FILTER_SANITIZE_ENCODED);
}
return $this;
}
/**
* Clean the url.
*
* @param string $fieldName
* @access public
* @return object fixer object.
*/
public function cleanURL($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_URL);
return $this;
}
/**
* Float fixer.
*
* @param string $fieldName
* @access public
* @return object fixer object.
*/
public function cleanFloat($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_NUMBER_FLOAT, FILTER_FLAG_ALLOW_FRACTION|FILTER_FLAG_ALLOW_THOUSAND);
return $this;
}
/**
* Int fixer.
*
* @param string $fieldName
* @access public
* @return object fixer object.
*/
public function cleanINT($fieldName = '')
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_NUMBER_INT);
return $this;
}
/**
* Special chars
*
* @param string $fieldName
* @access public
* @return object fixer object
*/
public function specialChars($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName)
{
if(empty($this->stripedFields) or !in_array($fieldName, $this->stripedFields)) $this->data->$fieldName = $this->specialArray($this->data->$fieldName);
}
return $this;
}
/**
* Special array
*
* @param mix $data
* @access public
* @return mix
*/
public function specialArray($data)
{
if(!is_array($data)) return htmlspecialchars($data, ENT_QUOTES);
foreach($data as &$value) $value = $this->specialArray($value);
return $data;
}
/**
* Strip tags
*
* @param string $fieldName
* @param string $allowedTags
* @access public
* @return object fixer object
*/
public function stripTags($fieldName, $allowedTags)
{
global $app;
$app->loadClass('purifier', true);
$config = HTMLPurifier_Config::createDefault();
$config->set('Filter.YouTube', 1);
/* Disable caching. */
$config->set('Cache.DefinitionImpl', null);
$purifier = new HTMLPurifier($config);
$def = $config->getHTMLDefinition(true);
$def->addAttribute('a', 'target', 'Enum#_blank,_self,_target,_top');
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName)
{
if(version_compare(phpversion(), '5.4', '<') and get_magic_quotes_gpc()) $this->data->$fieldName = stripslashes($this->data->$fieldName);
if(!in_array($fieldName, $this->stripedFields)) $this->data->$fieldName = $purifier->purify($this->data->$fieldName);
$this->stripedFields[] = $fieldName;
}
return $this;
}
/**
* Skip special chars.
*
* @param string $filename
* @access public
* @return object fixer object
*/
public function skipSpecial($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->stripedFields[] = $fieldName;
return $this;
}
/**
* Quote
*
* @param string $fieldName
* @access public
* @return object fixer object
*/
public function quote($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_SANITIZE_MAGIC_QUOTES);
return $this;
}
/**
* Set default value of some fileds.
*
* @param string $fields
* @param mixed $value
* @access public
* @return object fixer object
*/
public function setDefault($fields, $value)
{
$fields = strpos($fields, ',') ? explode(',', str_replace(' ', '', $fields)) : array($fields);
foreach($fields as $fieldName)if(!isset($this->data->$fieldName) or empty($this->data->$fieldName)) $this->data->$fieldName = $value;
return $this;
}
/**
* Set value of a filed on the condition is true.
*
* @param bool $condition
* @param string $fieldName
* @param string $value
* @access public
* @return object fixer object
*/
public function setIF($condition, $fieldName, $value)
{
if($condition) $this->data->$fieldName = $value;
return $this;
}
/**
* Set the value of a filed in force.
*
* @param string $fieldName
* @param mixed $value
* @access public
* @return object fixer object
*/
public function setForce($fieldName, $value)
{
$this->data->$fieldName = $value;
return $this;
}
/**
* Remove a field.
*
* @param string $fieldName
* @access public
* @return object fixer object
*/
public function remove($fieldName)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) unset($this->data->$fieldName);
return $this;
}
/**
* Remove a filed on the condition is true.
*
* @param bool $condition
* @param string $fields
* @access public
* @return object fixer object
*/
public function removeIF($condition, $fields)
{
$fields = $this->processFields($fields);
if($condition) foreach($fields as $fieldName) unset($this->data->$fieldName);
return $this;
}
/**
* Add an item to the data.
*
* @param string $fieldName
* @param mixed $value
* @access public
* @return object fixer object
*/
public function add($fieldName, $value)
{
$this->data->$fieldName = $value;
return $this;
}
/**
* Add an item to the data on the condition if true.
*
* @param bool $condition
* @param string $fieldName
* @param mixed $value
* @access public
* @return object fixer object
*/
public function addIF($condition, $fieldName, $value)
{
if($condition) $this->data->$fieldName = $value;
return $this;
}
/**
* Join the field.
*
* @param string $fieldName
* @param string $value
* @access public
* @return object fixer object
*/
public function join($fieldName, $value)
{
if(!isset($this->data->$fieldName) or !is_array($this->data->$fieldName)) return $this;
$this->data->$fieldName = join($value, $this->data->$fieldName);
return $this;
}
/**
* Call a function to fix it.
*
* @param string $fieldName
* @param string $func
* @access public
* @return object fixer object
*/
public function callFunc($fieldName, $func)
{
$fields = $this->processFields($fieldName);
foreach($fields as $fieldName) $this->data->$fieldName = filter_var($this->data->$fieldName, FILTER_CALLBACK, array('options' => $func));
return $this;
}
/**
* Get the data after fixing.
*
* If only one field, return it's value directly.
* More fields, remove other fields not in the list and return $data.
*
* @param string $fields the fields list.
* @access public
* @return mix
*/
public function get($fields = '')
{
$fields = str_replace(' ', '', trim($fields));
foreach($this->data as $field => $value) $this->specialChars($field);
if(empty($fields)) return $this->data;
if(strpos($fields, ',') === false) return $this->data->$fields;
$fields = array_flip(explode(',', $fields));
foreach($this->data as $field => $value)
{
if(!isset($fields[$field])) unset($this->data->$field);
if(!in_array($field, $this->stripedFields)) $this->data->$field = $this->specialChars($this->data->field);
}
return $this->data;
}
/**
* Process fields, if contains ',', split it to array. If not in $data, remove it.
*
* @param string $fields
* @access private
* @return array
*/
private function processFields($fields)
{
$fields = strpos($fields, ',') ? explode(',', str_replace(' ', '', $fields)) : array($fields);
foreach($fields as $key => $fieldName) if(!isset($this->data->$fieldName)) unset($fields[$key]);
return $fields;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

492
lib/pager/pager.class.php Executable file → Normal file
View File

@@ -1,5 +1,6 @@
<?php
/**
* ZenTaoPHP的分页类。
* The pager class file of ZenTaoPHP framework.
*
* The author disclaims copyright to this source code. In place of
@@ -9,497 +10,14 @@
* May you find forgiveness for yourself and forgive others.
* May you share freely, never taking more than you give.
*/
helper::import(dirname(dirname(__FILE__)) . '/base/pager/pager.class.php');
/**
* pager类.
* Pager class.
*
* @package framework
*/
class pager
class pager extends basePager
{
/**
* The default counts of per page.
*
* @public int
*/
const DEFAULT_REC_PRE_PAGE = 20;
/**
* The total counts.
*
* @var int
* @access public
*/
public $recTotal;
/**
* Record count per page.
*
* @var int
* @access public
*/
public $recPerPage;
/**
* The cookie name of recPerPage.
*
* @var string
* @access public
*/
public $pageCookie;
/**
* Page count.
*
* @var string
* @access public
*/
public $pageTotal;
/**
* Current page id.
*
* @var string
* @access public
*/
public $pageID;
/**
* The global $app.
*
* @var object
* @access private
*/
private $app;
/**
* The global $lang.
*
* @var object
* @access private
*/
private $lang;
/**
* Current module name.
*
* @var string
* @access private
*/
private $moduleName;
/**
* Current method.
*
* @var string
* @access private
*/
private $methodName;
/**
* The params.
*
* @private array
*/
private $params;
/**
* The construct function.
*
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return void
*/
public function __construct($recTotal = 0, $recPerPage = 20, $pageID = 1)
{
$this->setApp();
$this->setLang();
$this->setModuleName();
$this->setMethodName();
$this->setRecTotal($recTotal);
$this->setRecPerPage($recPerPage);
$this->setPageTotal();
$this->setPageID($pageID);
}
/**
* The factory function.
*
* @param int $recTotal
* @param int $recPerPage
* @param int $pageID
* @access public
* @return object
*/
public static function init($recTotal = 0, $recPerPage = 20, $pageID = 1)
{
return new pager($recTotal, $recPerPage, $pageID);
}
/**
* Set the recTotal property.
*
* @param int $recTotal
* @access public
* @return void
*/
public function setRecTotal($recTotal = 0)
{
$this->recTotal = (int)$recTotal;
}
/**
* Set the recTotal property.
*
* @param int $recPerPage
* @access public
* @return void
*/
public function setRecPerPage($recPerPage)
{
/* Set the cookie name. */
$this->pageCookie = 'pager' . ucfirst($this->app->getModuleName()) . ucfirst($this->app->getMethodName());
if(isset($_COOKIE[$this->pageCookie])) $recPerPage = $_COOKIE[$this->pageCookie];
$this->recPerPage = ($recPerPage > 0) ? $recPerPage : PAGER::DEFAULT_REC_PRE_PAGE;
}
/**
* Set the pageTotal property.
*
* @access public
* @return void
*/
public function setPageTotal()
{
$this->pageTotal = ceil($this->recTotal / $this->recPerPage);
}
/**
* Set the page id.
*
* @param int $pageID
* @access public
* @return void
*/
public function setPageID($pageID)
{
if($pageID > 0 and $pageID <= $this->pageTotal)
{
$this->pageID = $pageID;
}
else
{
$this->pageID = 1;
}
}
/**
* Set the $app property;
*
* @access private
* @return void
*/
private function setApp()
{
global $app;
$this->app = $app;
}
/**
* Set the $lang property.
*
* @access private
* @return void
*/
private function setLang()
{
global $lang;
$this->lang = $lang;
}
/**
* Set the $moduleName property.
*
* @access private
* @return void
*/
private function setModuleName()
{
$this->moduleName = $this->app->getModuleName();
}
/**
* Set the $methodName property.
*
* @access private
* @return void
*/
private function setMethodName()
{
$this->methodName = $this->app->getMethodName();
}
/**
* Get recTotal, recPerpage, pageID from the request params, and add them to params.
*
* @access private
* @return void
*/
private function setParams()
{
$this->params = $this->app->getParams();
foreach($this->params as $key => $value)
{
if(strtolower($key) == 'rectotal') $this->params[$key] = $this->recTotal;
if(strtolower($key) == 'recperpage') $this->params[$key] = $this->recPerPage;
if(strtolower($key) == 'pageid') $this->params[$key] = $this->pageID;
}
}
/**
* Create the limit string.
*
* @access public
* @return string
*/
public function limit()
{
$limit = '';
if($this->pageTotal > 1) $limit = ' limit ' . ($this->pageID - 1) * $this->recPerPage . ", $this->recPerPage";
return $limit;
}
/**
* Print the pager's html.
*
* @param string $align
* @param string $type
* @access public
* @return void
*/
public function show($align = 'right', $type = 'full')
{
echo $this->get($align, $type);
}
/**
* Get the pager html string.
*
* @param string $align
* @param string $type the pager type, full|short|shortest
* @access public
* @return string
*/
public function get($align = 'right', $type = 'full')
{
/* If the RecTotal is zero, return with no record. */
if($this->recTotal == 0) { return $type == 'mobile' ? '' : "<div style='float:$align; clear:none;' class='pager'>{$this->lang->pager->noRecord}</div>"; }
/* Set the params. */
$this->setParams();
/* Create the prePage and nextpage, all types have them. */
$pager = $this->createPrePage($type);
$pager .= $this->createNextPage($type);
/* The short and full type. */
if($type !== 'shortest' and $type !== 'mobile')
{
$pager = $this->createFirstPage() . $pager;
$pager .= $this->createLastPage();
}
if($type == 'mobile')
{
$position = $this->pageTotal == 1 ? '' : $this->pageID . '/' . $this->pageTotal;
$pager = $pager . ' ' . $position;
}
else if($type != 'full')
{
$pager = $this->pageID . '/' . $this->pageTotal . ' ' . $pager;
}
/* Only the full type . */
if($type == 'full')
{
$pager = $this->createDigest() . $pager;
$pager .= $this->createGoTo();
$pager .= $this->createRecPerPageJS();
}
return "<div style='float:$align; clear:none;' class='pager'>$pager</div>";
}
/**
* Create the digest code.
*
* @access private
* @return string
*/
private function createDigest()
{
return sprintf($this->lang->pager->digest, $this->recTotal, $this->createRecPerPageList(), $this->pageID, $this->pageTotal);
}
/**
* Create the first page.
*
* @access private
* @return string
*/
private function createFirstPage()
{
if($this->pageID == 1) return $this->lang->pager->first . ' ';
$this->params['pageID'] = 1;
return html::a(helper::createLink($this->moduleName, $this->methodName, $this->params), $this->lang->pager->first);
}
/**
* Create the pre page html.
*
* @access private
* @return string
*/
private function createPrePage($type = 'full')
{
if($type == 'mobile')
{
if($this->pageID == 1) return '';
$this->params['pageID'] = $this->pageID - 1;
return html::a(helper::createLink($this->moduleName, $this->methodName, $this->params), $this->lang->pager->pre, '', 'data-role="button" data-icon="arrow-l" data-iconpos="left" data-inline="true"');
}
else
{
if($this->pageID == 1) return $this->lang->pager->pre . ' ';
$this->params['pageID'] = $this->pageID - 1;
return html::a(helper::createLink($this->moduleName, $this->methodName, $this->params), $this->lang->pager->pre);
}
}
/**
* Create the next page html.
*
* @access private
* @return string
*/
private function createNextPage($type = 'full')
{
if($type == 'mobile')
{
if($this->pageID == $this->pageTotal) return '';
$this->params['pageID'] = $this->pageID + 1;
return html::a(helper::createLink($this->moduleName, $this->methodName, $this->params), $this->lang->pager->next, '', 'data-role="button" data-icon="arrow-r" data-iconpos="right" data-inline="true"');
}
else
{
if($this->pageID == $this->pageTotal) return $this->lang->pager->next . ' ';
$this->params['pageID'] = $this->pageID + 1;
return html::a(helper::createLink($this->moduleName, $this->methodName, $this->params), $this->lang->pager->next);
}
}
/**
* Create the last page
*
* @access private
* @return string
*/
private function createLastPage()
{
if($this->pageID == $this->pageTotal) return $this->lang->pager->last . ' ';
$this->params['pageID'] = $this->pageTotal;
return html::a(helper::createLink($this->moduleName, $this->methodName, $this->params), $this->lang->pager->last);
}
/**
* Create the select object of record perpage.
*
* @access private
* @return string
*/
private function createRecPerPageJS()
{
/* Replace the recTotal, recPerPage, pageID to special string, and then replace them with values by JS. */
$params = $this->params;
foreach($params as $key => $value)
{
if(strtolower($key) == 'rectotal') $params[$key] = '_recTotal_';
if(strtolower($key) == 'recperpage') $params[$key] = '_recPerPage_';
if(strtolower($key) == 'pageid') $params[$key] = '_pageID_';
}
$vars = '';
foreach($params as $key => $value) $vars .= "$key=$value&";
$vars = rtrim($vars, '&');
$js = <<<EOT
<script>
vars = '$vars';
pageCookie = '$this->pageCookie';
function submitPage(mode, perPage)
{
pageTotal = parseInt(document.getElementById('_pageTotal').value);
pageID = document.getElementById('_pageID').value;
recPerPage = document.getElementById('_recPerPage').getAttribute('data-value');
recTotal = document.getElementById('_recTotal').value;
if(mode == 'changePageID')
{
if(pageID > pageTotal) pageID = pageTotal;
if(pageID < 1) pageID = 1;
}
else if(mode == 'changeRecPerPage')
{
recPerPage = perPage;
pageID = 1;
}
$.cookie(pageCookie, recPerPage, {expires:config.cookieLife, path:config.webRoot});
vars = vars.replace('_recTotal_', recTotal)
vars = vars.replace('_recPerPage_', recPerPage)
vars = vars.replace('_pageID_', pageID);
location.href=createLink('$this->moduleName', '$this->methodName', vars);
}
</script>
EOT;
return $js;
}
/**
* Create the select list of RecPerPage.
*
* @access private
* @return string
*/
private function createRecPerPageList()
{
for($i = 5; $i <= 50; $i += 5) $range[$i] = $i;
$range[100] = 100;
$range[200] = 200;
$range[500] = 500;
$range[1000] = 1000;
$html = "<div class='dropdown dropup'><a href='javascript:;' data-toggle='dropdown' id='_recPerPage' data-value='{$this->recPerPage}'>" . (sprintf($this->lang->pager->recPerPage, $this->recPerPage)) . "<span class='caret'></span></a><ul class='dropdown-menu'>";
foreach ($range as $key => $value)
{
$html .= '<li' . ($this->recPerPage == $value ? " class='active'" : '') .'>' . "<a href='javascript:submitPage(\"changeRecPerPage\", $value)'>{$value}</a>" . '</li>';
}
$html .= '</ul></div>';
return $html;
}
/**
* Create the goto part html.
*
* @access private
* @return string
*/
private function createGoTo()
{
$goToHtml = "<input type='hidden' id='_recTotal' value='$this->recTotal' />\n";
$goToHtml .= "<input type='hidden' id='_pageTotal' value='$this->pageTotal' />\n";
$goToHtml .= "<input type='text' id='_pageID' value='$this->pageID' style='text-align:center;width:30px;' class='form-control' /> \n";
$goToHtml .= "<input type='button' id='goto' value='{$this->lang->pager->locate}' onclick='submitPage(\"changePageID\");' class='btn'/>";
return $goToHtml;
}
}

View File

@@ -41,6 +41,7 @@ class zdb
*/
public function dump($fileName, $tables = array())
{
global $config;
/* Init the return. */
$return = new stdclass();
$return->result = true;
@@ -48,11 +49,12 @@ class zdb
/* Get all tables in database. */
$allTables = array();
$stmt = $this->dbh->query('show tables');
while($table = $stmt->fetch(PDO::FETCH_ASSOC))
$stmt = $this->dbh->query("show full tables");
while($table = $stmt->fetch(PDO::FETCH_ASSOC))
{
$table = current($table);
$allTables[$table] = $table;
$tableName = $table["Tables_in_{$config->db->name}"];
$tableType = strtolower($table['Table_type']);
$allTables[$tableName] = $tableType == 'base table' ? 'table' : $tableType;
}
/* Dump all tables when tables is empty. */
@@ -75,15 +77,16 @@ class zdb
/* Open this file. */
$fp = fopen($fileName, 'w');
fwrite($fp, "SET NAMES utf8;\n");
foreach($tables as $table)
foreach($tables as $table => $tableType)
{
/* Check table exists. */
if(!isset($allTables[$table])) continue;
/* Create sql code. */
$backupSql = "DROP TABLE IF EXISTS `$table`;\n";
$backupSql .= $this->getSchemaSQL($table);
$backupSql = "DROP " . strtoupper($tableType) . " IF EXISTS `$table`;\n";
$backupSql .= $this->getSchemaSQL($table, $tableType);
fwrite($fp, $backupSql);
if($tableType != 'table') continue;
$rows = $this->dbh->query("select * from `$table`");
while($row = $rows->fetch(PDO::FETCH_ASSOC))
@@ -186,9 +189,10 @@ class zdb
* @access public
* @return string
*/
public function getSchemaSQL($table)
public function getSchemaSQL($table, $type = 'table')
{
$createSql = $this->dbh->query("show create table `$table`")->fetch(PDO::FETCH_ASSOC);
return $createSql['Create Table'] . ";\n";
$sql = "SHOW CREATE $type `$table`";
$createSql = $this->dbh->query($sql)->fetch(PDO::FETCH_ASSOC);
return $createSql['Create ' . ucfirst($type)] . ";\n";
}
}

View File

@@ -17,7 +17,6 @@ $lang->action->objectID = 'ID';
$lang->action->objectName = 'Name';
$lang->action->actor = 'Actor';
$lang->action->action = 'Action';
$lang->action->actionID = 'ActionID';
$lang->action->date = 'Date';
$lang->action->trash = 'Trash';
@@ -99,6 +98,10 @@ $lang->action->desc->diff1 = 'changed <strong><i>%s</i></strong>, old i
$lang->action->desc->diff2 = 'changed <strong><i>%s</i></strong>, the diff is:' . "\n" . "<blockquote>%s</blockquote>" . "\n<div class='hidden'>%s</div>";
$lang->action->desc->diff3 = "changed file's name %s to %s.";
/* The desc of actions for link and unlink related case. */
$lang->action->desc->linkrelatedcase = '$date, linked related case <strong>$extra</strong> by <strong>$actor</strong>.' . "\n";
$lang->action->desc->unlinkrelatedcase = '$date, unlinked related case <strong>$extra</strong> by <strong>$actor</strong>.' . "\n";
/* The action labels. */
$lang->action->label = new stdclass();
$lang->action->label->created = 'created';
@@ -131,6 +134,14 @@ $lang->action->label->changestatus = 'change status';
$lang->action->label->marked = 'edited';
$lang->action->label->linked2project = "link to {$lang->projectCommon}";
$lang->action->label->unlinkedfromproject = "unlik from {$lang->projectCommon}";
$lang->action->label->linkrelatedbug = "linked related Bug";
$lang->action->label->unlinkrelatedbug = "unlinked related Bug";
$lang->action->label->linkrelatedcase = "linked related case";
$lang->action->label->unlinkrelatedcase = "unlinked related case";
$lang->action->label->linkrelatedstory = "linked related story";
$lang->action->label->unlinkrelatedstory = "unlinked related story";
$lang->action->label->subdividestory = "subdivided story";
$lang->action->label->unlinkchildstory = "unlinked child story";
$lang->action->label->started = 'started';
$lang->action->label->restarted = 'continued';
$lang->action->label->recordestimate = 'Hours';

View File

@@ -17,7 +17,6 @@ $lang->action->objectID = '对象ID';
$lang->action->objectName = '对象名称';
$lang->action->actor = '操作者';
$lang->action->action = '动作';
$lang->action->actionID = '记录ID';
$lang->action->date = '日期';
$lang->action->trash = '回收站';
@@ -99,6 +98,10 @@ $lang->action->desc->diff1 = '修改了 <strong><i>%s</i></strong>
$lang->action->desc->diff2 = '修改了 <strong><i>%s</i></strong>,区别为:' . "\n" . "<blockquote>%s</blockquote>" . "\n<div class='hidden'>%s</div>";
$lang->action->desc->diff3 = '将文件名 %s 改为 %s 。' . "\n";
/* 关联用例和移除用例时的历史操作记录。*/
$lang->action->desc->linkrelatedcase = '$date, 由 <strong>$actor</strong> 关联相关用例 <strong>$extra</strong>。' . "\n";
$lang->action->desc->unlinkrelatedcase = '$date, 由 <strong>$actor</strong> 移除相关用例 <strong>$extra</strong>。' . "\n";
/* 用来显示动态信息。*/
$lang->action->label = new stdclass();
$lang->action->label->created = '创建';
@@ -131,6 +134,14 @@ $lang->action->label->changestatus = '修改状态';
$lang->action->label->marked = '编辑了';
$lang->action->label->linked2project = "关联{$lang->projectCommon}";
$lang->action->label->unlinkedfromproject = "移除{$lang->projectCommon}";
$lang->action->label->linkrelatedbug = "关联了相关Bug";
$lang->action->label->unlinkrelatedbug = "移除了相关Bug";
$lang->action->label->linkrelatedcase = "关联了相关用例";
$lang->action->label->unlinkrelatedcase = "移除了相关用例";
$lang->action->label->linkrelatedstory = "关联了相关需求";
$lang->action->label->unlinkrelatedstory = "移除了相关需求";
$lang->action->label->subdividestory = "细分了需求";
$lang->action->label->unlinkchildstory = "移除了细分需求";
$lang->action->label->started = '开始了';
$lang->action->label->restarted = '继续了';
$lang->action->label->recordestimate = '记录了工时';

View File

@@ -17,7 +17,6 @@ $lang->action->objectID = '對象ID';
$lang->action->objectName = '對象名稱';
$lang->action->actor = '操作者';
$lang->action->action = '動作';
$lang->action->actionID = '記錄ID';
$lang->action->date = '日期';
$lang->action->trash = '資源回收筒';
@@ -99,6 +98,10 @@ $lang->action->desc->diff1 = '修改了 <strong><i>%s</i></strong>
$lang->action->desc->diff2 = '修改了 <strong><i>%s</i></strong>,區別為:' . "\n" . "<blockquote>%s</blockquote>" . "\n<div class='hidden'>%s</div>";
$lang->action->desc->diff3 = '將檔案名 %s 改為 %s 。' . "\n";
/* 關聯用例和移除用例時的歷史操作記錄。*/
$lang->action->desc->linkrelatedcase = '$date, 由 <strong>$actor</strong> 關聯相關用例 <strong>$extra</strong>。' . "\n";
$lang->action->desc->unlinkrelatedcase = '$date, 由 <strong>$actor</strong> 移除相關用例 <strong>$extra</strong>。' . "\n";
/* 用來顯示動態信息。*/
$lang->action->label = new stdclass();
$lang->action->label->created = '創建';
@@ -131,6 +134,14 @@ $lang->action->label->changestatus = '修改狀態';
$lang->action->label->marked = '編輯了';
$lang->action->label->linked2project = "關聯{$lang->projectCommon}";
$lang->action->label->unlinkedfromproject = "移除{$lang->projectCommon}";
$lang->action->label->linkrelatedbug = "關聯了相關Bug";
$lang->action->label->unlinkrelatedbug = "移除了相關Bug";
$lang->action->label->linkrelatedcase = "關聯了相關用例";
$lang->action->label->unlinkrelatedcase = "移除了相關用例";
$lang->action->label->linkrelatedstory = "關聯了相關需求";
$lang->action->label->unlinkrelatedstory = "移除了相關需求";
$lang->action->label->subdividestory = "細分了需求";
$lang->action->label->unlinkchildstory = "移除了細分需求";
$lang->action->label->started = '開始了';
$lang->action->label->restarted = '繼續了';
$lang->action->label->recordestimate = '記錄了工時';

View File

@@ -241,8 +241,8 @@ class actionModel extends model
}
elseif($actionName == 'unlinkedfromproject')
{
$name = $this->dao->select('name')->from(TABLE_PRODUCT)->where('id')->eq($action->extra)->fetch('name');
if($name) $action->extra = html::a(helper::createLink('product', 'browse', "productID=$action->extra"), "#$action->extra " . $name);
$name = $this->dao->select('name')->from(TABLE_PROJECT)->where('id')->eq($action->extra)->fetch('name');
if($name) $action->extra = html::a(helper::createLink('project', 'story', "projectID=$action->extra"), "#$action->extra " . $name);
}
elseif($actionName == 'unlinkedfromplan')
{
@@ -269,6 +269,21 @@ class actionModel extends model
$name = $this->dao->select('name')->from(TABLE_TESTTASK)->where('id')->eq($action->objectID)->fetch('name');
if($name) $action->extra = html::a(helper::createLink('testtask', 'view', "testtaskID=$action->objectID"), "#$action->objectID " . $name);
}
elseif(($actionName == 'closed' and $action->objectType == 'story') or ($actionName == 'resolved' and $action->objectType == 'bug'))
{
$action->appendLink = '';
if(strpos($action->extra, ':')!== false)
{
list($extra, $id) = explode(':', $action->extra);
$action->extra = $extra;
if($id)
{
$table = $action->objectType == 'story' ? TABLE_STORY : TABLE_BUG;
$name = $this->dao->select('title')->from($table)->where('id')->eq($id)->fetch('title');
if($name) $action->appendLink = html::a(helper::createLink($action->objectType, 'view', "id=$id"), "#$id " . $name);
}
}
}
$action->history = isset($histories[$actionID]) ? $histories[$actionID] : array();
$action->comment = $this->file->setImgSize($action->comment, $this->config->action->commonImgSize);
$actions[$actionID] = $action;

View File

@@ -37,7 +37,7 @@
<tr class='text-center'>
<td><?php echo zget($lang->action->objectTypes, $action->objectType, '');?></td>
<td><?php echo $action->objectID;?></td>
<td class='text-left'><?php echo html::a($this->createLink($module, 'view', "id=$action->objectID"), $action->objectName);?></td>
<td class='text-left'><?php echo html::a($this->createLink($module, 'view', $action->objectType == 'user' ? "account={$action->objectName}" : "id={$action->objectID}"), $action->objectName);?></td>
<td><?php echo $users[$action->actor];?></td>
<td><?php echo $action->date;?></td>
<td>

View File

@@ -18,7 +18,7 @@ class admin extends control
*/
public function index()
{
$community = zget($this->config->global, 'community');
$community = zget($this->config->global, 'community', '');
if(!$community or $community == 'na')
{
$this->view->bind = false;
@@ -116,10 +116,10 @@ class admin extends control
*/
public function checkDB()
{
$tables = $this->dbh->query('SHOW TABLES')->fetchAll();
$tables = $this->dbh->query("show full tables where Table_Type != 'VIEW'")->fetchAll(PDO::FETCH_ASSOC);
foreach($tables as $table)
{
$tableName = current((array)$table);
$tableName = current($table);
$result = $this->dbh->query("REPAIR TABLE $tableName")->fetch();
echo "Repairing TABLE: " . $result->Table . "\t" . $result->Msg_type . ":" . $result->Msg_text . "\n";
}

View File

@@ -1,10 +1,10 @@
.cards {margin: 5% auto;}
.cards > .col-sm-6 {width: 25%; padding: 0 10px}
.card {height: 250px; position: relative; padding-top: 50px; border-radius: 6px; margin: 0}
.card {height: 250px; position: relative; padding-top: 50px; border-radius: 0; margin: 0}
.card > .media {position: absolute; left: 0; top: 0; width: 100%; background: #f1f1f1; height:50px; bottom: 0; overflow: hidden; z-index: 90; transition: all 0.4s;}
.card > .media > .icon {position: absolute; display: block; font-size: 56px; color: #ddd; z-index: 99; bottom: -10px; right: -6px; transition: all 0.4s;}
.card > .media > h5 {position: absolute; display: block; color: #666; z-index: 100; font-size: 14px; left: 15px; top: 10px; transition: all 0.4s;}
.card:hover > .media {background: #e5e5e5}
.card:hover > .media {background: #F8FAFE}
.card:hover > .media > .icon {color: #ccc}
.card .card-content {padding: 20px}
.card-content > ul {padding-left: 20px;}
@@ -13,5 +13,6 @@
.card a:hover {color: #000;}
#notice {margin: -20px -20px 0}
#titlebar {margin-top: 0; border-top: 1px solid #e5e5e5}
#titlebar {margin-top: -20px; border-top: none}
#notice + #titlebar {margin-top: 0; border-top: 1px solid #e5e5e5}
#proversion, #zentaotrain{color:#D2322D}

View File

@@ -12,18 +12,11 @@
$lang->admin->common = 'Admin';
$lang->admin->index = 'Index';
$lang->admin->checkDB = 'Repair data';
$lang->admin->company = 'Company';
$lang->admin->user = 'User';
$lang->admin->group = 'Group';
$lang->admin->sso = 'Integrate RanZhi';
$lang->admin->safeIndex = 'Safe';
$lang->admin->checkWeak = 'Check weak';
$lang->admin->welcome = 'Welcome to ZenTaoPMS.';
$lang->admin->browseCompany = 'Browse Company';
$lang->admin->info = new stdclass();
$lang->admin->info->caption = 'zentao information';
$lang->admin->info->version = 'The current version of the system is %s,';
$lang->admin->info->links = 'You can visit the following link:';
$lang->admin->info->account = 'Your account in zentao community is %s. ';
@@ -43,7 +36,6 @@ $lang->admin->register->success = "Register success";
$lang->admin->bind = new stdclass();
$lang->admin->bind->caption = 'Bind with community account';
$lang->admin->bind->action = 'bind';
$lang->admin->bind->success = "Bind success";
$lang->admin->safe = new stdclass();

View File

@@ -12,18 +12,11 @@
$lang->admin->common = '后台管理';
$lang->admin->index = '后台管理首页';
$lang->admin->checkDB = '检查数据库';
$lang->admin->company = '公司管理';
$lang->admin->user = '用户管理';
$lang->admin->group = '分组管理';
$lang->admin->sso = '然之集成';
$lang->admin->safeIndex = '安全';
$lang->admin->checkWeak = '弱口令检查';
$lang->admin->welcome = '欢迎使用禅道管理软件后台管理系统';
$lang->admin->browseCompany = '浏览公司';
$lang->admin->info = new stdclass();
$lang->admin->info->caption = '禅道系统信息';
$lang->admin->info->version = '当前系统的版本是%s';
$lang->admin->info->links = '您可以访问以下链接:';
$lang->admin->info->account = "您的禅道社区账户为%s。";
@@ -42,9 +35,8 @@ $lang->admin->register->bind = "如果您已经拥有社区账号,%s关
$lang->admin->register->success = "登记账户成功";
$lang->admin->bind = new stdclass();
$lang->admin->bind->caption = '关联社区账号';
$lang->admin->bind->action = '关联';
$lang->admin->bind->success = "关联账户成功";
$lang->admin->bind->caption = '关联社区账号';
$lang->admin->bind->success = "关联账户成功";
$lang->admin->safe = new stdclass();
$lang->admin->safe->common = '安全';

View File

@@ -12,18 +12,11 @@
$lang->admin->common = '後台管理';
$lang->admin->index = '後台管理首頁';
$lang->admin->checkDB = '檢查資料庫';
$lang->admin->company = '公司管理';
$lang->admin->user = '用戶管理';
$lang->admin->group = '分組管理';
$lang->admin->sso = '然之整合';
$lang->admin->safeIndex = '安全';
$lang->admin->checkWeak = '弱口令檢查';
$lang->admin->welcome = '歡迎使用禪道管理軟件後台管理系統';
$lang->admin->browseCompany = '瀏覽公司';
$lang->admin->info = new stdclass();
$lang->admin->info->caption = '禪道系統信息';
$lang->admin->info->version = '當前系統的版本是%s';
$lang->admin->info->links = '您可以訪問以下連結:';
$lang->admin->info->account = "您的禪道社區賬戶為%s。";
@@ -42,9 +35,8 @@ $lang->admin->register->bind = "如果您已經擁有社區賬號,%s關
$lang->admin->register->success = "登記賬戶成功";
$lang->admin->bind = new stdclass();
$lang->admin->bind->caption = '關聯社區賬號';
$lang->admin->bind->action = '關聯';
$lang->admin->bind->success = "關聯賬戶成功";
$lang->admin->bind->caption = '關聯社區賬號';
$lang->admin->bind->success = "關聯賬戶成功";
$lang->admin->safe = new stdclass();
$lang->admin->safe->common = '安全';

View File

@@ -39,7 +39,7 @@
<td><?php echo $user->mobile?></td>
<td><?php echo $user->birthday?></td>
<td><?php echo $lang->admin->safe->reasonList[$user->weakReason];?></td>
<td><?php common::printIcon('user', 'edit', "userID=" . helper::safe64Encode($user->account), '', 'list');?></td>
<td><?php common::printIcon('user', 'edit', "userID=$user->id", '', 'list');?></td>
</tr>
<?php endforeach;?>
</tbody>

View File

@@ -43,6 +43,6 @@
</tr>
</table>
</form>
<div class='alert alert-info'><?php echo $lang->sso->help?></div>
<div class='alert alert-info mg-0'><?php echo $lang->sso->help?></div>
</div>
<?php include '../../common/view/footer.html.php';?>

View File

@@ -77,7 +77,7 @@ class backup extends control
*/
public function backup($reload = 'no')
{
set_time_limit(0);
set_time_limit(7200);
$fileName = date('YmdHis') . mt_rand(0, 9);
$result = $this->backup->backSQL($this->backupPath . $fileName . '.sql.php');
if(!$result->result)
@@ -149,7 +149,7 @@ class backup extends control
die(js::confirm($this->lang->backup->confirmRestore, inlink('restore', "fileName=$fileName&confirm=yes"), inlink('index'), 'self', 'parent'));
}
set_time_limit(0);
set_time_limit(7200);
/* Restore database. */
$this->backup->removeFileHeader($this->backupPath . $fileName . '.sql.php');

View File

@@ -8,7 +8,6 @@ $lang->backup->restore = 'Restore';
$lang->backup->change = 'Change hold days';
$lang->backup->changeAB = 'change';
$lang->backup->name = 'Name';
$lang->backup->time = 'Time';
$lang->backup->files = 'Files';
$lang->backup->size = 'Size';

View File

@@ -8,7 +8,6 @@ $lang->backup->restore = '还原';
$lang->backup->change = '修改保留时间';
$lang->backup->changeAB = '修改';
$lang->backup->name = '文件名称';
$lang->backup->time = '备份时间';
$lang->backup->files = '备份文件';
$lang->backup->size = '大小';

View File

@@ -8,7 +8,6 @@ $lang->backup->restore = '還原';
$lang->backup->change = '修改保留時間';
$lang->backup->changeAB = '修改';
$lang->backup->name = '檔案名稱';
$lang->backup->time = '備份時間';
$lang->backup->files = '備份檔案';
$lang->backup->size = '大小';

View File

@@ -55,9 +55,14 @@ class block extends control
$params = json_decode(base64_decode($params));
$sso = base64_decode($this->get->sso);
if(!isset($this->app->user)) $this->app->user = new stdclass();
if(!isset($this->app->user->account) or $this->app->user->account != $params->account) $this->app->user->account = $params->account;
$this->app->user = $this->dao->select('*')->from(TABLE_USER)->where('ranzhi')->eq($params->account)->fetch();
if(empty($this->app->user))
{
$this->app->user = new stdclass();
$this->app->user->account = 'guest';
}
$this->viewType = (isset($params->viewType) and $params->viewType == 'json') ? 'json' : 'html';
$this->params = $params;
$this->view->sso = $sso;
$this->view->sign = strpos($sso, '&') === false ? '?' : '&';
@@ -65,6 +70,23 @@ class block extends control
$func = 'print' . ucfirst($code) . 'Block';
$this->$func();
if($this->viewType == 'json')
{
unset($this->view->app);
unset($this->view->config);
unset($this->view->lang);
unset($this->view->header);
unset($this->view->position);
unset($this->view->moduleTree);
$output['status'] = is_object($this->view) ? 'success' : 'fail';
$output['data'] = json_encode($this->view);
$output['md5'] = md5(json_encode($this->view));
die(json_encode($output));
}
$this->display();
}
}
@@ -76,8 +98,8 @@ class block extends control
*/
public function printTodoBlock()
{
$this->view->todos = $this->loadModel('todo')->getList('all', $this->app->user->account, 'wait, doing', $this->params->num);
$this->display();
$this->view->todos = $this->loadModel('todo')->getList('all', $this->app->user->account, 'wait, doing', $this->viewType == 'json' ? 0 : $this->params->num);
$this->view->listLink = $this->view->sso . $this->view->sign . 'referer=' . base64_encode($this->createLink('my', 'todo', "type=all"));
}
/**
@@ -88,8 +110,8 @@ class block extends control
*/
public function printTaskBlock()
{
$this->view->tasks = $this->loadModel('task')->getUserTasks($this->app->user->account, $this->params->type, $this->params->num, null, $this->params->orderBy);
$this->display();
$this->view->tasks = $this->loadModel('task')->getUserTasks($this->app->user->account, $this->params->type, $this->viewType == 'json' ? 0 : $this->params->num, null, $this->params->orderBy);
$this->view->listLink = $this->view->sso . $this->view->sign . 'referer=' . base64_encode($this->createLink('my', 'task', "type={$this->params->type}"));
}
/**
@@ -100,9 +122,8 @@ class block extends control
*/
public function printBugBlock()
{
$this->view->bugs = $this->loadModel('bug')->getUserBugs($this->app->user->account, $this->params->type, $this->params->orderBy, $this->params->num);
$this->display();
$this->view->bugs = $this->loadModel('bug')->getUserBugs($this->app->user->account, $this->params->type, $this->params->orderBy, $this->viewType == 'json' ? 0 : $this->params->num);
$this->view->listLink = $this->view->sso . $this->view->sign . 'referer=' . base64_encode($this->createLink('my', 'bug', "type={$this->params->type}"));
}
/**
@@ -127,17 +148,20 @@ class block extends control
->andWhere('t3.status')->ne('done')
->andWhere('t3.deleted')->eq(0)
->andWhere('t2.deleted')->eq(0)
->orderBy($this->params->orderBy)->limit($this->params->num)->fetchAll();
->orderBy($this->params->orderBy)
->beginIF($this->viewType != 'json')->limit($this->params->num)->fi()
->fetchAll();
}
elseif($this->params->type == 'openedbyme')
{
$cases = $this->dao->findByOpenedBy($this->app->user->account)->from(TABLE_CASE)
->andWhere('deleted')->eq(0)
->orderBy($this->params->orderBy)->limit($this->params->num)->fetchAll();
->orderBy($this->params->orderBy)
->beginIF($this->viewType != 'json')->limit($this->params->num)->fi()
->fetchAll();
}
$this->view->cases = $cases;
$this->display();
$this->view->cases = $cases;
$this->view->listLink = $this->view->sso . $this->view->sign . 'referer=' . base64_encode($this->createLink('my', 'testcase', "type={$this->params->type}"));
}
/**
@@ -150,8 +174,8 @@ class block extends control
{
$this->app->loadClass('pager', $static = true);
$pager = pager::init(0, $this->params->num, 1);
$this->view->stories = $this->loadModel('story')->getUserStories($this->app->user->account, $this->params->type, $this->params->orderBy, $pager);
$this->display();
$this->view->stories = $this->loadModel('story')->getUserStories($this->app->user->account, $this->params->type, $this->params->orderBy, $this->viewType != 'json' ? $pager : '');
$this->view->listLink = $this->view->sso . $this->view->sign . 'referer=' . base64_encode($this->createLink('my', 'story', "type={$this->params->type}"));
}
/**
@@ -164,9 +188,8 @@ class block extends control
{
$this->app->loadClass('pager', $static = true);
$pager = pager::init(0, $this->params->num, 1);
$this->view->productStats = $this->loadModel('product')->getStats('code_asc', $pager);
$this->display();
$this->view->productStats = $this->loadModel('product')->getStats('order_desc', $this->viewType != 'json' ? $pager : '');
$this->view->listLink = $this->view->sso . $this->view->sign . 'referer=' . base64_encode($this->createLink('product', 'index', "locate=no&productID=" . current($this->view->productStats)->id));
}
/**
@@ -179,8 +202,7 @@ class block extends control
{
$this->app->loadClass('pager', $static = true);
$pager = pager::init(0, $this->params->num, 1);
$this->view->projectStats = $this->loadModel('project')->getProjectStats($status = 'undone', $productID = 0, $branch = 0, $itemCounts = 30, $orderBy = 'code', $pager);
$this->display();
$this->view->projectStats = $this->loadModel('project')->getProjectStats($status = 'undone', $productID = 0, $branch = 0, $itemCounts = 30, $orderBy = 'order_desc', $this->viewType != 'json' ? $pager : '');
$this->view->listLink = $this->view->sso . $this->view->sign . 'referer=' . base64_encode($this->createLink('my', 'project'));
}
}

View File

@@ -13,8 +13,6 @@ $lang->block = new stdclass();
$lang->block->num = 'Number';
$lang->block->type = 'Type';
$lang->block->orderBy = 'Order';
$lang->block->status = 'Status';
$lang->block->actions = 'Action';
$lang->block->availableBlocks = new stdclass();

View File

@@ -13,8 +13,6 @@ $lang->block = new stdclass();
$lang->block->num = '数量';
$lang->block->type = '类型';
$lang->block->orderBy = '排序';
$lang->block->status = '状态';
$lang->block->actions = '操作';
$lang->block->availableBlocks = new stdclass();

View File

@@ -13,8 +13,6 @@ $lang->block = new stdclass();
$lang->block->num = '數量';
$lang->block->type = '類型';
$lang->block->orderBy = '排序';
$lang->block->status = '狀態';
$lang->block->actions = '操作';
$lang->block->availableBlocks = new stdclass();

View File

@@ -29,4 +29,8 @@
</tr>
<?php endforeach;?>
</table>
<script>$('.block-bug').dataTable();</script>
<p class='hide block-bug-link'><?php echo $listLink;?></p>
<script>
$('.block-bug').dataTable();
$('.block-bug-link').closest('.panel').find('.panel-heading .more').attr('href', $('.block-bug-link').html());
</script>

View File

@@ -33,4 +33,8 @@
</tr>
<?php endforeach;?>
</table>
<script>$('.block-case').dataTable();</script>
<p class='hide block-case-link'><?php echo $listLink;?></p>
<script>
$('.block-case').dataTable();
$('.block-case-link').closest('.panel').find('.panel-heading .more').attr('href', $('.block-case-link').html());
</script>

View File

@@ -39,4 +39,8 @@
<?php endforeach;?>
</tbody>
</table>
<script>$('.block-product').dataTable();</script>
<p class='hide block-product-link'><?php echo $listLink;?></p>
<script>
$('.block-product').dataTable();
$('.block-product-link').closest('.panel').find('.panel-heading .more').attr('href', $('.block-product-link').html());
</script>

View File

@@ -38,4 +38,8 @@
<?php endforeach;?>
</tbody>
</table>
<script>$('.block-project').dataTable();</script>
<p class='hide block-project-link'><?php echo $listLink;?></p>
<script>
$('.block-project').dataTable();
$('.block-project-link').closest('.panel').find('.panel-heading .more').attr('href', $('.block-project-link').html());
</script>

View File

@@ -33,4 +33,8 @@
</tr>
<?php endforeach;?>
</table>
<script>$('.block-story').dataTable();</script>
<p class='hide block-story-link'><?php echo $listLink;?></p>
<script>
$('.block-story').dataTable();
$('.block-story-link').closest('.panel').find('.panel-heading .more').attr('href', $('.block-story-link').html());
</script>

View File

@@ -36,4 +36,8 @@ td.delayed{background: #e84e0f!important; color: white;}
</tr>
<?php endforeach;?>
</table>
<script>$('.block-task').dataTable();</script>
<p class='hide block-task-link'><?php echo $listLink;?></p>
<script>
$('.block-task').dataTable();
$('.block-task-link').closest('.panel').find('.panel-heading .more').attr('href', $('.block-task-link').html());
</script>

View File

@@ -31,4 +31,8 @@
</tr>
<?php endforeach;?>
</table>
<script>$('.block-todo').dataTable();</script>
<p class='hide block-todo-link'><?php echo $listLink;?></p>
<script>
$('.block-todo').dataTable();
$('.block-todo-link').closest('.panel').find('.panel-heading .more').attr('href', $('.block-todo-link').html());
</script>

View File

@@ -115,6 +115,6 @@ class branch extends control
$branches = $this->branch->getPairs($productID);
if($oldBranch) $branches = array($oldBranch => $branches[$oldBranch]);
die(html::select('branch', $branches, '', "class='form-control' onchange='loadBranch()'"));
die(html::select('branch', $branches, '', "class='form-control' onchange='loadBranch(this)'"));
}
}

View File

@@ -4,6 +4,6 @@ $lang->branch->manage = 'Branch manage';
$lang->branch->delete = 'Branch delete';
$lang->branch->manageTitle = '%s Manage';
$lang->branch->all = 'All';
$lang->branch->all = 'All ';
$lang->branch->confirmDelete = '%branch% deletion, will affect the %branch% of the demand, module, plan, release, Bug, testcase and so on, please consider carefully. Are you sure delete the %branch%?';

View File

@@ -43,7 +43,7 @@ class branchModel extends model
if(strpos($params, 'noempty') === false)
{
$product = $this->loadModel('product')->getById($productID);
if($product->type == 'normal') return array();
if(!$product or $product->type == 'normal') return array();
$branches = array('0' => $this->lang->branch->all . $this->lang->product->branchName[$product->type]) + $branches;
}

View File

@@ -52,6 +52,7 @@ class bug extends control
* Browse bugs.
*
* @param int $productID
* @param string $branch
* @param string $browseType
* @param int $param
* @param string $orderBy
@@ -63,10 +64,10 @@ class bug extends control
*/
public function browse($productID = 0, $branch = '', $browseType = 'unclosed', $param = 0, $orderBy = '', $recTotal = 0, $recPerPage = 20, $pageID = 1)
{
/* Set browseType, productID, moduleID and queryID. */
/* Set browseType, productID, moduleID, queryID and branch. */
$browseType = strtolower($browseType);
$productID = $this->product->saveState($productID, $this->products);
if($branch === '') $branch = $this->session->branch;
$branch = ($branch == '') ? $this->session->branch : $branch;
$moduleID = ($browseType == 'bymodule') ? (int)$param : 0;
$queryID = ($browseType == 'bysearch') ? (int)$param : 0;
@@ -86,98 +87,43 @@ class bug extends control
if($this->app->getViewType() == 'mhtml') $recPerPage = 10;
$pager = pager::init($recTotal, $recPerPage, $pageID);
$projects = $this->loadModel('project')->getPairs();
$projects[0] = '';
/* Get projects. */
$projects = $this->loadModel('project')->getPairs('empty');
/* Get bugs. */
$bugs = array();
if($browseType == 'all') $bugs = $this->bug->getAllBugs($productID, $branch, $projects, $sort, $pager);
elseif($browseType == "bymodule")
{
$childModuleIds = $this->tree->getAllChildId($moduleID);
$bugs = $this->bug->getModuleBugs($productID, $branch, $childModuleIds, $projects, $sort, $pager);
}
elseif($browseType == 'assigntome') $bugs = $this->bug->getByAssigntome($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'openedbyme') $bugs = $this->bug->getByOpenedbyme($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'resolvedbyme') $bugs = $this->bug->getByResolvedbyme($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'assigntonull') $bugs = $this->bug->getByAssigntonull($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'unconfirmed') $bugs = $this->bug->getUnconfirmed($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'unresolved') $bugs = $this->bug->getByStatus($productID, $branch, $projects, 'unresolved', $sort, $pager);
elseif($browseType == 'unclosed') $bugs = $this->bug->getByStatus($productID, $branch, $projects, 'unclosed', $sort, $pager);
elseif($browseType == 'toclosed') $bugs = $this->bug->getByStatus($productID, $branch, $projects, 'toclosed', $sort, $pager);
elseif($browseType == 'longlifebugs') $bugs = $this->bug->getByLonglifebugs($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'postponedbugs') $bugs = $this->bug->getByPostponedbugs($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'needconfirm') $bugs = $this->bug->getByNeedconfirm($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'bysearch') $bugs = $this->bug->getBySearch($productID, $queryID, $sort, $pager, $branch);
$bugs = $this->bug->getBugs($productID, $projects, $branch, $browseType, $moduleID, $queryID, $sort, $pager);
/* Process the sql, get the conditon partion, save it to session. */
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'bug', $browseType == 'needconfirm' ? false : true);
/* Process bug for check story changed. */
$bugs = $this->loadModel('story')->checkNeedConfirm($bugs);
/* Build the search form. */
$this->config->bug->search['actionURL'] = $this->createLink('bug', 'browse', "productID=$productID&branch=$branch&browseType=bySearch&queryID=myQueryID");
$this->config->bug->search['queryID'] = $queryID;
$this->config->bug->search['params']['product']['values'] = array($productID => $this->products[$productID], 'all' => $this->lang->bug->allProduct);
$this->config->bug->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs($productID);
$this->config->bug->search['params']['module']['values'] = $this->tree->getOptionMenu($productID, $viewType = 'bug', $startModuleID = 0);
$this->config->bug->search['params']['project']['values'] = $this->product->getProjectPairs($productID);
$this->config->bug->search['params']['openedBuild']['values'] = $this->loadModel('build')->getProductBuildPairs($productID, 0, $params = '');
$this->config->bug->search['params']['resolvedBuild']['values'] = $this->config->bug->search['params']['openedBuild']['values'];
if($this->session->currentProductType == 'normal')
{
unset($this->config->bug->search['fields']['branch']);
unset($this->config->bug->search['params']['branch']);
}
else
{
$this->config->bug->search['fields']['branch'] = $this->lang->product->branch;
$this->config->bug->search['params']['branch']['values'] = array('' => '') + $this->loadModel('branch')->getPairs($productID, 'noempty');
}
$this->loadModel('search')->setSearchParams($this->config->bug->search);
$users = $this->user->getPairs('noletter');
$bugs = $this->loadModel('story')->checkNeedConfirm($bugs);
/* Process the openedBuild and resolvedBuild fields. */
$productIdList = array();
foreach($bugs as $bug) $productIdList[$bug->id] = $bug->product;
$builds = $this->loadModel('build')->getProductBuildPairs(array_unique($productIdList), 0, $params = '');
foreach($bugs as $key => $bug)
{
$openBuildIdList = explode(',', $bug->openedBuild);
$openedBuild = '';
foreach($openBuildIdList as $buildID)
{
$openedBuild .= isset($builds[$buildID]) ? $builds[$buildID] : $buildID;
$openedBuild .= ',';
}
$bug->openedBuild = rtrim($openedBuild, ',');
$bug->resolvedBuild = isset($builds[$bug->resolvedBuild]) ? $builds[$bug->resolvedBuild] : $bug->resolvedBuild;
}
$bugs = $this->bug->processBuildForBugs($bugs);
$memberPairs = $this->user->getPairs('noletter|nodeleted');
$title = $this->products[$productID] . $this->lang->colon . $this->lang->bug->common;
$position[] = html::a($this->createLink('bug', 'browse', "productID=$productID"), $this->products[$productID]);
$position[] = $this->lang->bug->common;
/* Build the search form. */
$actionURL = $this->createLink('bug', 'browse', "productID=$productID&branch=$branch&browseType=bySearch&queryID=myQueryID");
$this->bug->buildSearchForm($productID, $this->products, $queryID, $actionURL);
$this->view->title = $title;
$this->view->position = $position;
$this->view->productID = $productID;
$this->view->productName = $this->products[$productID];
$this->view->builds = $this->loadModel('build')->getProductBuildPairs($productID);
$this->view->moduleTree = $this->tree->getTreeMenu($productID, $viewType = 'bug', $startModuleID = 0, array('treeModel', 'createBugLink'), '', $branch);
$this->view->browseType = $browseType;
$this->view->bugs = $bugs;
$this->view->users = $users;
$this->view->pager = $pager;
$this->view->param = $param;
$this->view->orderBy = $orderBy;
$this->view->moduleID = $moduleID;
$this->view->memberPairs = $memberPairs;
$this->view->branch = $branch;
$this->view->branches = $this->loadModel('branch')->getPairs($productID);
/* Set view. */
$this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->bug->common;
$this->view->position[] = html::a($this->createLink('bug', 'browse', "productID=$productID"), $this->products[$productID]);
$this->view->position[] = $this->lang->bug->common;
$this->view->productID = $productID;
$this->view->productName = $this->products[$productID];
$this->view->builds = $this->loadModel('build')->getProductBuildPairs($productID);
$this->view->moduleTree = $this->tree->getTreeMenu($productID, $viewType = 'bug', $startModuleID = 0, array('treeModel', 'createBugLink'), '', $branch);
$this->view->browseType = $browseType;
$this->view->bugs = $bugs;
$this->view->users = $this->user->getPairs('noletter');
$this->view->pager = $pager;
$this->view->param = $param;
$this->view->orderBy = $orderBy;
$this->view->moduleID = $moduleID;
$this->view->memberPairs = $this->user->getPairs('noletter|nodeleted');
$this->view->branch = $branch;
$this->view->branches = $this->loadModel('branch')->getPairs($productID);
$this->display();
}
@@ -351,7 +297,7 @@ class bug extends control
$this->view->productName = $this->products[$productID];
$this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'bug', $startModuleID = 0, $branch);
$this->view->stories = $stories;
$this->view->projects = $this->product->getProjectPairs($productID, $branch, $params = 'nodeleted');
$this->view->projects = $this->product->getProjectPairs($productID, $branch ? "0,$branch" : 0, $params = 'nodeleted');
$this->view->builds = $builds;
$this->view->moduleID = $moduleID;
$this->view->projectID = $projectID;
@@ -416,19 +362,14 @@ class bug extends control
if($this->session->bugImagesFile)
{
$extractPath = $this->session->bugImagesFile;
if(is_dir($extractPath))
$files = $this->session->bugImagesFile;
foreach($files as $fileName => $file)
{
$titles = array();
$files = glob($extractPath . '/*');
sort($files);
foreach($files as $fileName)
{
$fileName = basename($fileName);
$titles[$fileName] = preg_replace('/^\d+_/', '', pathinfo($fileName, PATHINFO_FILENAME));
}
$this->view->titles = $titles;
$title = $file['title'];
$titles[$title] = $fileName;
}
krsort($titles);
$this->view->titles = $titles;
}
$this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->bug->batchCreate;
@@ -439,7 +380,7 @@ class bug extends control
$this->view->stories = $stories;
$this->view->builds = $builds;
$this->view->users = $this->user->getPairs('nodeleted,devfirst');
$this->view->projects = $this->product->getProjectPairs($productID, $branch, $params = 'nodeleted');
$this->view->projects = $this->product->getProjectPairs($productID, $branch ? "0,$branch" : 0, $params = 'nodeleted');
$this->view->projectID = $projectID;
$this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'bug', $startModuleID = 0, $branch);
$this->view->moduleID = $moduleID;
@@ -585,7 +526,7 @@ class bug extends control
$this->view->plans = $this->loadModel('productplan')->getPairs($productID, $bug->branch);
$this->view->moduleOptionMenu = $this->tree->getOptionMenu($productID, $viewType = 'bug', $startModuleID = 0, $bug->branch);
$this->view->currentModuleID = $currentModuleID;
$this->view->projects = $this->product->getProjectPairs($bug->product);
$this->view->projects = $this->product->getProjectPairs($bug->product, $bug->branch ? "0,{$bug->branch}" : 0, 'nodeleted');
$this->view->stories = $bug->project ? $this->story->getProjectStoryPairs($bug->project) : $this->story->getProductStoryPairs($bug->product, $bug->branch);
$this->view->branches = $this->session->currentProductType == 'normal' ? array() : $this->loadModel('branch')->getPairs($bug->product);
$this->view->tasks = $this->task->getProjectTaskPairs($bug->project);
@@ -691,7 +632,7 @@ class bug extends control
$bug = $this->bug->getById($bugID);
/* Set menu. */
$this->bug->setMenu($this->products, $bug->product);
$this->bug->setMenu($this->products, $bug->product, $bug->branch);
if(!empty($_POST))
{
@@ -764,7 +705,7 @@ class bug extends control
$bug = $this->bug->getById($bugID);
$productID = $bug->product;
$this->bug->setMenu($this->products, $productID);
$this->bug->setMenu($this->products, $productID, $bug->branch);
$this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->bug->confirmBug;
$this->view->position[] = html::a($this->createLink('bug', 'browse', "productID=$productID"), $this->products[$productID]);
@@ -811,7 +752,7 @@ class bug extends control
$files = $this->loadModel('file')->saveUpload('bug', $bugID);
$fileAction = !empty($files) ? $this->lang->addFiles . join(',', $files) . "\n" : '';
$actionID = $this->action->create('bug', $bugID, 'Resolved', $fileAction . $this->post->comment, $this->post->resolution);
$actionID = $this->action->create('bug', $bugID, 'Resolved', $fileAction . $this->post->comment, $this->post->resolution . ($this->post->duplicateBug ? ':' . (int)$this->post->duplicateBug : ''));
$this->sendmail($bugID, $actionID);
$bug = $this->bug->getById($bugID);
@@ -832,7 +773,7 @@ class bug extends control
$assignedTo = $bug->openedBy;
if(!isset($users[$assignedTo])) $assignedTo = $this->bug->getModuleOwner($bug->module, $productID);
$this->bug->setMenu($this->products, $productID);
$this->bug->setMenu($this->products, $productID, $bug->branch);
$this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->bug->resolve;
$this->view->position[] = html::a($this->createLink('bug', 'browse', "productID=$productID"), $this->products[$productID]);
@@ -889,7 +830,7 @@ class bug extends control
$bug = $this->bug->getById($bugID);
$productID = $bug->product;
$this->bug->setMenu($this->products, $productID);
$this->bug->setMenu($this->products, $productID, $bug->branch);
$this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->bug->activate;
$this->view->position[] = html::a($this->createLink('bug', 'browse', "productID=$productID"), $this->products[$productID]);
@@ -924,7 +865,7 @@ class bug extends control
$bug = $this->bug->getById($bugID);
$productID = $bug->product;
$this->bug->setMenu($this->products, $productID);
$this->bug->setMenu($this->products, $productID, $bug->branch);
$this->view->title = $this->products[$productID] . $this->lang->colon . $this->lang->bug->close;
$this->view->position[] = html::a($this->createLink('bug', 'browse', "productID=$productID"), $this->products[$productID]);
@@ -936,6 +877,92 @@ class bug extends control
$this->display();
}
/**
* Link related bugs.
*
* @param int $bugID
* @param string $browseType
* @param int $param
* @access public
* @return void
*/
public function linkBugs($bugID, $browseType = '', $param = 0)
{
/* Link bugs. */
if(!empty($_POST))
{
$this->bug->linkBugs($bugID);
if(isonlybody()) die(js::closeModal('parent.parent', '', "function(){parent.parent.loadLinkBugs('$bugID')}"));
die(js::locate($this->createLink('bug', 'edit', "bugID=$bugID"), 'parent'));
}
/* Get bug and queryID. */
$bug = $this->bug->getById($bugID);
$queryID = ($browseType == 'bySearch') ? (int)$param : 0;
/* Set the menu. */
$this->bug->setMenu($this->products, $bug->product, $bug->branch);
/* Build the search form. */
$actionURL = $this->createLink('bug', 'linkBugs', "bugID=$bugID&browseType=bySearch&queryID=myQueryID", '', true);
$this->bug->buildSearchForm($bug->product, $this->products, $queryID, $actionURL);
/* Get bugs to link. */
$bugs2Link = $this->bug->getBugs2Link($bugID, $browseType, $queryID);
/* Assign. */
$this->view->title = $this->lang->bug->linkBugs . "BUG #$bug->id $bug->title - " . $this->products[$bug->product];
$this->view->position[] = html::a($this->createLink('product', 'view', "productID=$bug->product"), $this->products[$bug->product]);
$this->view->position[] = html::a($this->createLink('bug', 'view', "bugID=$bugID"), $bug->title);
$this->view->position[] = $this->lang->bug->linkBugs;
$this->view->bug = $bug;
$this->view->bugs2Link = $bugs2Link;
$this->view->users = $this->loadModel('user')->getPairs('noletter');
$this->display();
}
/**
* AJAX: get linkBugs.
*
* @param int $bugID
* @access public
* @return string
*/
public function ajaxGetLinkBugs($bugID)
{
/* Get linkbugs. */
$bugs = $this->bug->getLinkBugs($bugID);
/* Build linkBug list.*/
$output = '';
foreach($bugs as $bugId => $bugTitle)
{
$output .= '<li>';
$output .= html::a(inlink('view', "bugID=$bugId"), "#$bugId " . $bugTitle, '_blank');
$output .= html::a("javascript:unlinkBug($bugID, $bugId)", '<i class="icon-remove"></i>', '', "title='{$this->lang->unlink}' style='float:right'");
$output .= '</li>';
}
die($output);
}
/**
* Unlink related bug.
*
* @param int $bugID
* @param int $bug2Unlink
* @access public
* @return string
*/
public function unlinkBug($bugID, $bug2Unlink = 0)
{
/* Unlink related bug. */
$this->bug->unlinkBug($bugID, $bug2Unlink);
die('success');
}
/**
* Batch close bugs.
*
@@ -1182,7 +1209,8 @@ class bug extends control
}
/* Get action info. */
$action = $this->action->getById($actionID);
$actions = $this->action->getList('bug', $bugID);
$action = zget($actions, $actionID, null);
$history = $this->action->getHistory($actionID);
$action->history = isset($history[$actionID]) ? $history[$actionID] : array();
if(strtolower($action->action) == 'opened') $action->comment = $bug->steps;

View File

@@ -16,6 +16,17 @@
#module_chosen.chosen-container .chosen-drop {min-width: 400px; border-top: 1px solid #ddd!important}
#openedBuild_chosen.chosen-container .chosen-drop {min-width: 450px; border-top: 1px solid #ddd!important}
#module + .chosen-container-single .chosen-single,
#task + .chosen-container-single .chosen-single,
#openedBuild + .chosen-container-multi .chosen-choices {border-top-left-radius: 0; border-bottom-left-radius: 0;}
#openedBuild + .chosen-container-multi .chosen-choices,
#mailto + .chosen-container-multi .chosen-choices {border-top-right-radius: 0; border-bottom-right-radius: 0;}
.row .col-sm-8{width:75%;}
.row .col-sm-4{padding-left:0px; width:25%;}
.dropdown-pris > .btn {background-color: #fff; text-shadow: none}
/* Keep label has same width as relative task label*/
#keywordsAddonLabel:before {content: '空'; color: transparent}
#contactListGroup .input-group-btn > .btn {margin-left: -1px!important;}
.minw-80px {min-width: 80px;}

View File

@@ -2,3 +2,4 @@
.col-side .chosen-container {width: 218px!important}
.col-side .chosen-container[id^="openedBuild"] {width: 172px!important}
.col-side .chosen-container[id^="resolvedBuild"] {width: 172px!important}
#linkBugBox > li {margin-left:-56px}

View File

@@ -0,0 +1 @@
#linkBugsForm {min-height: 300px}

View File

@@ -25,7 +25,7 @@ function loadProjectBuilds(productID, projectID, index)
$.get(link, function(builds)
{
var row = $('#buildBox' + index).parents('tbody').find('tr').size()
var row = $('#buildBox' + index).closest('tbody').find('tr').size()
do
{
var selected = $('#buildBox' + index).find('select').val();
@@ -49,9 +49,9 @@ $(document).on('click', '.chosen-with-drop', function()
var select = $(this).prev('select');
if($(select).val() == 'ditto')
{
var index = $(select).parents('td').index();
var row = $(select).parents('tr').index();
var table = $(select).parents('tr').parent();
var index = $(select).closest('td').index();
var row = $(select).closest('tr').index();
var table = $(select).closest('tr').parent();
var value = '';
for(i = row - 1; i >= 0; i--)
{
@@ -66,9 +66,9 @@ $(document).on('mousedown', 'select', function()
{
if($(this).val() == 'ditto')
{
var index = $(this).parents('td').index();
var row = $(this).parents('tr').index();
var table = $(this).parents('tr').parent();
var index = $(this).closest('td').index();
var row = $(this).closest('tr').index();
var table = $(this).closest('tr').parent();
var value = '';
for(i = row - 1; i >= 0; i--)
{

View File

@@ -11,7 +11,7 @@ $(document).ready(function()
}).on('keyup change paste', 'input', function()
{
var val = $(this).val().toLowerCase();
var $options = $(this).parents('ul.dropdown-menu.with-search').find('.option');
var $options = $(this).closest('ul.dropdown-menu.with-search').find('.option');
if(val == '') return $options.removeClass('hide');
$options.each(function()
{
@@ -19,4 +19,6 @@ $(document).ready(function()
$option.toggleClass('hide', $option.text().toString().toLowerCase().indexOf(val) < 0 && $option.data('key').toString().toLowerCase().indexOf(val) < 0);
});
});
setTimeout(function(){fixedTfootAction('#bugForm')}, 100);
setTimeout(function(){fixedTheadOfList('#bugList')}, 100);
});

View File

@@ -426,15 +426,15 @@ function notice()
}
else
{
if($('#buildBox').parents('tr').find('td').size() > 1)
if($('#buildBox').closest('tr').find('td').size() > 1)
{
$('#buildBox').parents('td').next().attr('id', 'buildBoxActions');
$('#buildBox').parents('td').next().html(html);
$('#buildBox').closest('td').next().attr('id', 'buildBoxActions');
$('#buildBox').closest('td').next().html(html);
}
else
{
html = "<td id='buildBoxActions'>" + html + '</td>';
$('#buildBox').parents('td').after(html);
$('#buildBox').closest('td').after(html);
}
}
}

View File

@@ -55,47 +55,6 @@ function setAssignedTo(moduleID, productID)
});
}
$(function()
{
if($('#project').val()) loadProjectRelated($('#project').val());
$('#saveTplBtn').on('click', function()
{
var content = $('#steps').val();
bootbox.prompt(setTemplateTitle, function(r)
{
if(!r || !content) return;
saveTemplateLink = createLink('bug', 'saveTemplate');
$.post(saveTemplateLink, {title:r, content:content}, function(data)
{
$('#tplBox').html(data);
});
});
});
});
// /* Save template. */
// KindEditor.plugin('savetemplate', function(K)
// {
// var self = this, name = 'savetemplate';
// self.plugin.savetemplate =
// {
// click: function(id)
// {
// content = self.html();
// bootbox.prompt(setTemplateTitle, function(r)
// {
// if(!r || !content) return;
// saveTemplateLink = createLink('bug', 'saveTemplate');
// $.post(saveTemplateLink, {title:r, content:content}, function(data)
// {
// $('#tplBox').html(data);
// });
// });
// }
// };
// self.clickToolbar(name, self.plugin.savetemplate.click);
// });
/* Set template. */
function setTemplate(templateID)
{
@@ -112,3 +71,31 @@ function deleteTemplate(templateID)
hiddenwin.location.href = createLink('bug', 'deleteTemplate', 'templateID=' + templateID);
$('#tplBox' + templateID).addClass('hidden');
}
$(function()
{
if($('#project').val()) loadProjectRelated($('#project').val());
$('#saveTplBtn').on('click', function()
{
var content = $('#steps').val();
bootbox.prompt(setTemplateTitle, function(r)
{
if(!r || !content) return;
saveTemplateLink = createLink('bug', 'saveTemplate');
$.post(saveTemplateLink, {title:r, content:content}, function(data)
{
$('#tplBox').html(data);
});
});
});
$('[data-toggle=tooltip]').tooltip();
// ajust style for file box
var ajustFilebox = function()
{
applyCssStyle('.fileBox > tbody > tr > td:first-child {transition: none; width: ' + ($('#contactListGroup').width() - 2) + 'px}', 'filebox')
};
ajustFilebox();
$(window).resize(ajustFilebox);
});

View File

@@ -54,3 +54,33 @@ function loadModuleRelated()
productID = $('#product').val();
setStories(moduleID, productID);
}
/**
* Unlink related bug.
*
* @param int $bugID
* @param int $bug2Unlink
* @access public
* @return void
*/
function unlinkBug(bugID, bug2Unlink)
{
link = createLink('bug', 'unlinkBug', 'bugID=' + bugID + '&bug2Unlink=' + bug2Unlink);
$.get(link, function(data)
{
if(data == 'success') $('#linkBugBox').load(createLink('bug', 'ajaxGetLinkBugs', 'bugID=' + bugID));
});
}
/**
* Load linkBugs.
*
* @param int $bugID
* @access public
* @return void
*/
function loadLinkBugs(bugID)
{
bugLink = createLink('bug', 'ajaxGetLinkBugs', 'bugID=' + bugID);
$('#linkBugBox').load(bugLink);
}

View File

@@ -9,6 +9,6 @@ $(function()
});
};
resizeChartTable();
fixTableHead('.table-wrapper');
fixedTableHead('.table-wrapper');
$(window).resize(resizeChartTable);
});

View File

@@ -15,10 +15,8 @@ $lang->bug->id = 'ID';
$lang->bug->product = $lang->productCommon;
$lang->bug->productplan = 'Plan';
$lang->bug->module = 'Module';
$lang->bug->path = 'Path';
$lang->bug->project = $lang->projectCommon;
$lang->bug->story = 'Story';
$lang->bug->storyVersion = 'Story Version';
$lang->bug->task = 'Task';
$lang->bug->title = 'Title';
$lang->bug->severity = 'Severity';
@@ -26,22 +24,16 @@ $lang->bug->severityAB = 'S';
$lang->bug->pri = 'Priority';
$lang->bug->type = 'Type';
$lang->bug->os = 'OS';
$lang->bug->plan = 'Plan';
$lang->bug->hardware = 'Hardware';
$lang->bug->browser = 'Browser';
$lang->bug->machine = 'Machine';
$lang->bug->found = 'How found';
$lang->bug->steps = 'Steps';
$lang->bug->status = 'Status';
$lang->bug->statusAB = 'Status';
$lang->bug->activatedCount = 'Activated count';
$lang->bug->activatedCountAB = 'Activated count';
$lang->bug->confirmed = 'Confirmed';
$lang->bug->toTask = 'To task';
$lang->bug->toStory = 'To story';
$lang->bug->mailto = 'Mailto';
$lang->bug->openedBy = 'Opened By';
$lang->bug->openedByAB = 'Opened';
$lang->bug->openedDate = 'Opened date';
$lang->bug->openedDateAB = 'Date';
$lang->bug->openedBuild = 'Opened Build';
@@ -58,8 +50,9 @@ $lang->bug->closedBy = 'Closed By';
$lang->bug->closedDate = 'Closed Date';
$lang->bug->duplicateBug = 'Duplicate';
$lang->bug->lastEditedBy = 'Last Edited By';
$lang->bug->lastEditedDate = 'Last Edited Date';
$lang->bug->linkBug = 'Related';
$lang->bug->linkBugs = 'Link related bugs';
$lang->bug->unlinkBug = 'Unlink related bug';
$lang->bug->case = 'Case';
$lang->bug->files = 'Files';
$lang->bug->keywords = 'Keywords';
@@ -91,15 +84,9 @@ $lang->bug->delete = 'Delete Bug';
$lang->bug->deleted = 'Deleted';
$lang->bug->saveTemplate = 'Save template';
$lang->bug->deleteTemplate = 'Delete template';
$lang->bug->customFields = 'Custom';
$lang->bug->restoreDefault = 'Default';
$lang->bug->ajaxGetUserBugs = 'API: My Bugs';
$lang->bug->ajaxGetModuleOwner = 'API: Get module default owner';
$lang->bug->confirmStoryChange = 'Confirm Story Change';
/* Browse tabs. */
$lang->bug->selectProduct = "Select {$lang->productCommon}";
$lang->bug->byModule = 'ByModule';
$lang->bug->assignToMe = 'MyBugs';
$lang->bug->openedByMe = 'MyOpen';
$lang->bug->resolvedByMe = 'MyResolve';
@@ -111,51 +98,30 @@ $lang->bug->unclosed = 'Unclosed';
$lang->bug->longLifeBugs = 'Longlife';
$lang->bug->postponedBugs = 'Postponed';
$lang->bug->allBugs = 'Allbug';
$lang->bug->moduleBugs = 'ByModule';
$lang->bug->byQuery = 'Search';
$lang->bug->needConfirm = 'StoryChanged';
$lang->bug->allProduct = "All {$lang->productCommon}s";
$lang->bug->ditto = 'Ditto';
/* Labels. */
$lang->bug->lblProductAndModule = "{$lang->productCommon}&Module";
$lang->bug->lblProjectAndTask = "{$lang->projectCommon}&Task";
$lang->bug->lblStory = 'Story';
$lang->bug->lblTypeAndSeverity = 'Type&Severity';
$lang->bug->lblSystemBrowserAndHardware = 'OS&Browser';
$lang->bug->lblAssignedTo = 'Assigned to';
$lang->bug->lblMailto = 'Mailto';
$lang->bug->lblLastEdited = 'Last edited';
$lang->bug->lblResolved = 'Resolved';
$lang->bug->lblAllFields = 'All Fields';
$lang->bug->lblCustomFields = 'Custom Fields';
$lang->bug->allUsers = 'All users';
$lang->bug->allBuilds = 'All';
/* Legends. */
$lang->bug->legendBasicInfo = 'Basic info';
$lang->bug->legendMailto = 'Mailto';
$lang->bug->legendAttatch = 'Files';
$lang->bug->legendLinkBugs = 'Related bug';
$lang->bug->legendPrjStoryTask = "{$lang->projectCommon}, story & task";
$lang->bug->legendCases = 'Related case';
$lang->bug->legendSteps = 'Steps';
$lang->bug->legendAction = 'Action';
$lang->bug->legendHistory = 'History';
$lang->bug->legendComment = 'Comment';
$lang->bug->legendLife = 'Lifetime';
$lang->bug->legendMisc = 'Misc';
/* Action buttons. */
$lang->bug->buttonConfirm = 'Confirm';
$lang->bug->buttonCopy = 'Copy';
$lang->bug->buttonAssign = 'Assgin';
$lang->bug->buttonEdit = 'Edit';
$lang->bug->buttonActivate = 'Activate';
$lang->bug->buttonResolve = 'Resolve';
$lang->bug->buttonClose = 'Close';
$lang->bug->buttonToList = 'Back';
$lang->bug->buttonCreateTestcase = 'Create Case';
/* Confirm messags. */
$lang->bug->confirmChangeProduct = "Change {$lang->productCommon} will change {$lang->projectCommon}, task and story also, are you sure?";
@@ -354,11 +320,13 @@ $lang->bug->report->bugHistories->graph->xAxisName = 'Histories';
/* 操作记录。*/
$lang->bug->action = new stdclass();
$lang->bug->action->resolved = array('main' => '$date, Resolved by <strong>$actor</strong>, resolution is <strong>$extra</strong>.', 'extra' => 'resolutionList');
$lang->bug->action->resolved = array('main' => '$date, Resolved by <strong>$actor</strong>, resolution is <strong>$extra</strong> $appendLink.', 'extra' => 'resolutionList');
$lang->bug->action->tostory = array('main' => '$date, To story by <strong>$actor</strong>, ID is <strong>$extra</strong>.');
$lang->bug->action->totask = array('main' => '$date, To task by <strong>$actor</strong>, ID is <strong>$extra</strong>.');
$lang->bug->action->linked2plan = array('main' => '$date, Link to plan by <strong>$actor</strong>,ID is <strong>$extra</strong>。');
$lang->bug->action->unlinkedfromplan = array('main' => '$date, Unlink from plan <strong>$extra</strong> by <strong>$actor</strong>.');
$lang->bug->action->linkrelatedbug = array('main' => '$date, link related bug <strong>$extra</strong> by <strong>$actor</strong>.');
$lang->bug->action->unlinkrelatedbug = array('main' => '$date, unlink related bug <strong>$extra</strong> by <strong>$actor</strong>.');
$lang->bug->placeholder = new stdclass();
$lang->bug->placeholder->chooseBuilds = 'Choose builds...';

View File

@@ -15,10 +15,8 @@ $lang->bug->id = 'Bug编号';
$lang->bug->product = '所属' . $lang->productCommon;
$lang->bug->productplan = '所属计划';
$lang->bug->module = '所属模块';
$lang->bug->path = '模块路径';
$lang->bug->project = '所属' . $lang->projectCommon;
$lang->bug->story = '相关需求';
$lang->bug->storyVersion = '需求版本';
$lang->bug->task = '相关任务';
$lang->bug->title = 'Bug标题';
$lang->bug->severity = '严重程度';
@@ -26,22 +24,16 @@ $lang->bug->severityAB = '级别';
$lang->bug->pri = '优先级';
$lang->bug->type = 'Bug类型';
$lang->bug->os = '操作系统';
$lang->bug->plan = '所属计划';
$lang->bug->hardware = '硬件平台';
$lang->bug->browser = '浏览器';
$lang->bug->machine = '机器硬件';
$lang->bug->found = '如何发现';
$lang->bug->steps = '重现步骤';
$lang->bug->status = 'Bug状态';
$lang->bug->statusAB = '状态';
$lang->bug->activatedCount = '激活次数';
$lang->bug->activatedCountAB = '激活次数';
$lang->bug->confirmed = '是否确认';
$lang->bug->toTask = '转任务';
$lang->bug->toStory = '转需求';
$lang->bug->mailto = '抄送给';
$lang->bug->openedBy = '由谁创建';
$lang->bug->openedByAB = '创建';
$lang->bug->openedDate = '创建日期';
$lang->bug->openedDateAB = '创建日期';
$lang->bug->openedBuild = '影响版本';
@@ -58,8 +50,9 @@ $lang->bug->closedBy = '由谁关闭';
$lang->bug->closedDate = '关闭日期';
$lang->bug->duplicateBug = '重复ID';
$lang->bug->lastEditedBy = '最后修改者';
$lang->bug->lastEditedDate = '最后修改日期';
$lang->bug->linkBug = '相关Bug';
$lang->bug->linkBugs = '关联相关Bug';
$lang->bug->unlinkBug = '移除相关Bug';
$lang->bug->case = '相关用例';
$lang->bug->files = '附件';
$lang->bug->keywords = '关键词';
@@ -91,15 +84,9 @@ $lang->bug->delete = '删除';
$lang->bug->deleted = '已删除';
$lang->bug->saveTemplate = '保存模板';
$lang->bug->deleteTemplate = '删除模板';
$lang->bug->customFields = '自定义字段';
$lang->bug->restoreDefault = '恢复默认';
$lang->bug->ajaxGetUserBugs = '接口:我的Bug';
$lang->bug->ajaxGetModuleOwner = '接口:获得模块的默认指派人';
$lang->bug->confirmStoryChange = '确认需求变动';
/* 查询条件列表。*/
$lang->bug->selectProduct = '请选择' . $lang->productCommon;
$lang->bug->byModule = '按模块';
$lang->bug->assignToMe = '指派给我';
$lang->bug->openedByMe = '由我创建';
$lang->bug->resolvedByMe = '由我解决';
@@ -111,51 +98,30 @@ $lang->bug->unclosed = '未关闭';
$lang->bug->longLifeBugs = '久未处理';
$lang->bug->postponedBugs = '被延期';
$lang->bug->allBugs = '所有';
$lang->bug->moduleBugs = '按模块';
$lang->bug->byQuery = '搜索';
$lang->bug->needConfirm = '需求变动';
$lang->bug->allProduct = '所有' . $lang->productCommon;
$lang->bug->ditto = '同上';
/* 页面标签。*/
$lang->bug->lblProductAndModule = $lang->productCommon . '模块';
$lang->bug->lblProjectAndTask = $lang->projectCommon . '任务';
$lang->bug->lblStory = '相关需求';
$lang->bug->lblTypeAndSeverity = '类型/严重程度';
$lang->bug->lblSystemBrowserAndHardware = '系统/浏览器';
$lang->bug->lblAssignedTo = '当前指派';
$lang->bug->lblMailto = '抄送给';
$lang->bug->lblLastEdited = '最后修改';
$lang->bug->lblResolved = '由谁解决';
$lang->bug->lblAllFields = '所有字段';
$lang->bug->lblCustomFields = '自定义字段';
$lang->bug->allUsers = '所有用户';
$lang->bug->allBuilds = '所有';
/* legend列表。*/
$lang->bug->legendBasicInfo = '基本信息';
$lang->bug->legendMailto = '抄送给';
$lang->bug->legendAttatch = '附件';
$lang->bug->legendLinkBugs = '相关Bug';
$lang->bug->legendPrjStoryTask = $lang->projectCommon . '/需求/任务';
$lang->bug->legendCases = '相关用例';
$lang->bug->legendSteps = '重现步骤';
$lang->bug->legendAction = '操作';
$lang->bug->legendHistory = '历史记录';
$lang->bug->legendComment = '备注';
$lang->bug->legendLife = 'BUG的一生';
$lang->bug->legendMisc = '其他相关';
/* 功能按钮。*/
$lang->bug->buttonConfirm = '确认';
$lang->bug->buttonCopy = '复制';
$lang->bug->buttonAssign = '指派';
$lang->bug->buttonEdit = '编辑';
$lang->bug->buttonActivate = '激活';
$lang->bug->buttonResolve = '解决';
$lang->bug->buttonClose = '关闭';
$lang->bug->buttonToList = '返回';
$lang->bug->buttonCreateTestcase = '转用例';
/* 交互提示。*/
$lang->bug->confirmChangeProduct = "修改{$lang->productCommon}会导致相应的{$lang->projectCommon}、需求和任务发生变化,确定吗?";
@@ -354,11 +320,13 @@ $lang->bug->report->bugHistories->graph->xAxisName = '处理步骤';
/* 操作记录。*/
$lang->bug->action = new stdclass();
$lang->bug->action->resolved = array('main' => '$date, 由 <strong>$actor</strong> 解决,方案为 <strong>$extra</strong>。', 'extra' => 'resolutionList');
$lang->bug->action->resolved = array('main' => '$date, 由 <strong>$actor</strong> 解决,方案为 <strong>$extra</strong> $appendLink。', 'extra' => 'resolutionList');
$lang->bug->action->tostory = array('main' => '$date, 由 <strong>$actor</strong> 转为<strong>需求</strong>,编号为 <strong>$extra</strong>。');
$lang->bug->action->totask = array('main' => '$date, 由 <strong>$actor</strong> 导入为<strong>任务</strong>,编号为 <strong>$extra</strong>。');
$lang->bug->action->linked2plan = array('main' => '$date, 由 <strong>$actor</strong> 关联到计划 <strong>$extra</strong>。');
$lang->bug->action->unlinkedfromplan = array('main' => '$date, 由 <strong>$actor</strong> 从计划 <strong>$extra</strong> 移除。');
$lang->bug->action->linkrelatedbug = array('main' => '$date, 由 <strong>$actor</strong> 关联相关Bug <strong>$extra</strong>。');
$lang->bug->action->unlinkrelatedbug = array('main' => '$date, 由 <strong>$actor</strong> 移除相关Bug <strong>$extra</strong>。');
$lang->bug->placeholder = new stdclass();
$lang->bug->placeholder->chooseBuilds = '选择相关版本...';

View File

@@ -15,10 +15,8 @@ $lang->bug->id = 'Bug編號';
$lang->bug->product = '所屬' . $lang->productCommon;
$lang->bug->productplan = '所屬計劃';
$lang->bug->module = '所屬模組';
$lang->bug->path = '模組路徑';
$lang->bug->project = '所屬' . $lang->projectCommon;
$lang->bug->story = '相關需求';
$lang->bug->storyVersion = '需求版本';
$lang->bug->task = '相關任務';
$lang->bug->title = 'Bug標題';
$lang->bug->severity = '嚴重程度';
@@ -26,22 +24,16 @@ $lang->bug->severityAB = '級別';
$lang->bug->pri = '優先順序';
$lang->bug->type = 'Bug類型';
$lang->bug->os = '操作系統';
$lang->bug->plan = '所屬計劃';
$lang->bug->hardware = '硬件平台';
$lang->bug->browser = '瀏覽器';
$lang->bug->machine = '機器硬件';
$lang->bug->found = '如何發現';
$lang->bug->steps = '重現步驟';
$lang->bug->status = 'Bug狀態';
$lang->bug->statusAB = '狀態';
$lang->bug->activatedCount = '激活次數';
$lang->bug->activatedCountAB = '激活次數';
$lang->bug->confirmed = '是否確認';
$lang->bug->toTask = '轉任務';
$lang->bug->toStory = '轉需求';
$lang->bug->mailto = '抄送給';
$lang->bug->openedBy = '由誰創建';
$lang->bug->openedByAB = '創建';
$lang->bug->openedDate = '創建日期';
$lang->bug->openedDateAB = '創建日期';
$lang->bug->openedBuild = '影響版本';
@@ -58,8 +50,9 @@ $lang->bug->closedBy = '由誰關閉';
$lang->bug->closedDate = '關閉日期';
$lang->bug->duplicateBug = '重複ID';
$lang->bug->lastEditedBy = '最後修改者';
$lang->bug->lastEditedDate = '最後修改日期';
$lang->bug->linkBug = '相關Bug';
$lang->bug->linkBugs = '關聯相關Bug';
$lang->bug->unlinkBug = '移除相關Bug';
$lang->bug->case = '相關用例';
$lang->bug->files = '附件';
$lang->bug->keywords = '關鍵詞';
@@ -91,15 +84,9 @@ $lang->bug->delete = '刪除';
$lang->bug->deleted = '已刪除';
$lang->bug->saveTemplate = '保存模板';
$lang->bug->deleteTemplate = '刪除模板';
$lang->bug->customFields = '自定義欄位';
$lang->bug->restoreDefault = '恢復預設';
$lang->bug->ajaxGetUserBugs = '介面:我的Bug';
$lang->bug->ajaxGetModuleOwner = '介面:獲得模組的預設指派人';
$lang->bug->confirmStoryChange = '確認需求變動';
/* 查詢條件列表。*/
$lang->bug->selectProduct = '請選擇' . $lang->productCommon;
$lang->bug->byModule = '按模組';
$lang->bug->assignToMe = '指派給我';
$lang->bug->openedByMe = '由我創建';
$lang->bug->resolvedByMe = '由我解決';
@@ -111,51 +98,30 @@ $lang->bug->unclosed = '未關閉';
$lang->bug->longLifeBugs = '久未處理';
$lang->bug->postponedBugs = '被延期';
$lang->bug->allBugs = '所有';
$lang->bug->moduleBugs = '按模組';
$lang->bug->byQuery = '搜索';
$lang->bug->needConfirm = '需求變動';
$lang->bug->allProduct = '所有' . $lang->productCommon;
$lang->bug->ditto = '同上';
/* 頁面標籤。*/
$lang->bug->lblProductAndModule = $lang->productCommon . '模組';
$lang->bug->lblProjectAndTask = $lang->projectCommon . '任務';
$lang->bug->lblStory = '相關需求';
$lang->bug->lblTypeAndSeverity = '類型/嚴重程度';
$lang->bug->lblSystemBrowserAndHardware = '系統/瀏覽器';
$lang->bug->lblAssignedTo = '當前指派';
$lang->bug->lblMailto = '抄送給';
$lang->bug->lblLastEdited = '最後修改';
$lang->bug->lblResolved = '由誰解決';
$lang->bug->lblAllFields = '所有欄位';
$lang->bug->lblCustomFields = '自定義欄位';
$lang->bug->allUsers = '所有用戶';
$lang->bug->allBuilds = '所有';
/* legend列表。*/
$lang->bug->legendBasicInfo = '基本信息';
$lang->bug->legendMailto = '抄送給';
$lang->bug->legendAttatch = '附件';
$lang->bug->legendLinkBugs = '相關Bug';
$lang->bug->legendPrjStoryTask = $lang->projectCommon . '/需求/任務';
$lang->bug->legendCases = '相關用例';
$lang->bug->legendSteps = '重現步驟';
$lang->bug->legendAction = '操作';
$lang->bug->legendHistory = '歷史記錄';
$lang->bug->legendComment = '備註';
$lang->bug->legendLife = 'BUG的一生';
$lang->bug->legendMisc = '其他相關';
/* 功能按鈕。*/
$lang->bug->buttonConfirm = '確認';
$lang->bug->buttonCopy = '複製';
$lang->bug->buttonAssign = '指派';
$lang->bug->buttonEdit = '編輯';
$lang->bug->buttonActivate = '激活';
$lang->bug->buttonResolve = '解決';
$lang->bug->buttonClose = '關閉';
$lang->bug->buttonToList = '返回';
$lang->bug->buttonCreateTestcase = '轉用例';
/* 交互提示。*/
$lang->bug->confirmChangeProduct = "修改{$lang->productCommon}會導致相應的{$lang->projectCommon}、需求和任務發生變化,確定嗎?";
@@ -354,11 +320,13 @@ $lang->bug->report->bugHistories->graph->xAxisName = '處理步驟';
/* 操作記錄。*/
$lang->bug->action = new stdclass();
$lang->bug->action->resolved = array('main' => '$date, 由 <strong>$actor</strong> 解決,方案為 <strong>$extra</strong>。', 'extra' => 'resolutionList');
$lang->bug->action->resolved = array('main' => '$date, 由 <strong>$actor</strong> 解決,方案為 <strong>$extra</strong> $appendLink。', 'extra' => 'resolutionList');
$lang->bug->action->tostory = array('main' => '$date, 由 <strong>$actor</strong> 轉為<strong>需求</strong>,編號為 <strong>$extra</strong>。');
$lang->bug->action->totask = array('main' => '$date, 由 <strong>$actor</strong> 導入為<strong>任務</strong>,編號為 <strong>$extra</strong>。');
$lang->bug->action->linked2plan = array('main' => '$date, 由 <strong>$actor</strong> 關聯到計劃 <strong>$extra</strong>。');
$lang->bug->action->unlinkedfromplan = array('main' => '$date, 由 <strong>$actor</strong> 從計劃 <strong>$extra</strong> 移除。');
$lang->bug->action->linkrelatedbug = array('main' => '$date, 由 <strong>$actor</strong> 關聯相關Bug <strong>$extra</strong>。');
$lang->bug->action->unlinkrelatedbug = array('main' => '$date, 由 <strong>$actor</strong> 移除相關Bug <strong>$extra</strong>。');
$lang->bug->placeholder = new stdclass();
$lang->bug->placeholder->chooseBuilds = '選擇相關版本...';

View File

@@ -140,14 +140,11 @@ class bugModel extends model
if(!empty($data->uploadImage[$i]))
{
$fileName = htmlspecialchars_decode($data->uploadImage[$i]);
$realPath = $this->session->bugImagesFile . $fileName;
$fileName = $data->uploadImage[$i];
$file = $this->session->bugImagesFile[$fileName];
$file = array();
$file['extension'] = $this->file->getExtension($fileName);
$file['pathname'] = $this->file->setPathName($i, $file['extension']);
$file['title'] = str_replace(".{$file['extension']}", '', $fileName);
$file['size'] = filesize($realPath);
$realPath = $file['realpath'];
unset($file['realpath']);
if(rename($realPath, $this->file->savePath . $file['pathname']))
{
if(in_array($file['extension'], $this->config->file->imageExtensions))
@@ -194,13 +191,52 @@ class bugModel extends model
if(!empty($data->uploadImage) and $this->session->bugImagesFile)
{
$classFile = $this->app->loadClass('zfile');
if(is_dir($this->session->bugImagesFile)) $classFile->removeDir($this->session->bugImagesFile);
$file = current($_SESSION['bugImagesFile']);
$realPath = dirname($file['realpath']);
if(is_dir($realPath)) $classFile->removeDir($realPath);
unset($_SESSION['bugImagesFile']);
}
return $actions;
}
/**
* Get bugs.
*
* @param int $productID
* @param array $projects
* @param int $branch
* @param string $browseType
* @param int $moduleID
* @param int $queryID
* @param string $sort
* @param object $pager
* @access public
* @return array
*/
public function getBugs($productID, $projects, $branch, $browseType, $moduleID, $queryID, $sort, $pager)
{
/* Get bugs by browse type. */
if($browseType == 'all') $bugs = $this->getAllBugs($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'bymodule') $bugs = $this->getModuleBugs($productID, $branch, $this->loadModel('tree')->getAllChildId($moduleID), $projects, $sort, $pager);
elseif($browseType == 'assigntome') $bugs = $this->getByAssigntome($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'openedbyme') $bugs = $this->getByOpenedbyme($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'resolvedbyme') $bugs = $this->getByResolvedbyme($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'assigntonull') $bugs = $this->getByAssigntonull($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'unconfirmed') $bugs = $this->getUnconfirmed($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'unresolved') $bugs = $this->getByStatus($productID, $branch, $projects, 'unresolved', $sort, $pager);
elseif($browseType == 'unclosed') $bugs = $this->getByStatus($productID, $branch, $projects, 'unclosed', $sort, $pager);
elseif($browseType == 'toclosed') $bugs = $this->getByStatus($productID, $branch, $projects, 'toclosed', $sort, $pager);
elseif($browseType == 'longlifebugs') $bugs = $this->getByLonglifebugs($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'postponedbugs') $bugs = $this->getByPostponedbugs($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'needconfirm') $bugs = $this->getByNeedconfirm($productID, $branch, $projects, $sort, $pager);
elseif($browseType == 'bysearch') $bugs = $this->getBySearch($productID, $queryID, $sort, $pager, $branch);
if($bugs) return $bugs;
return array();
}
/**
* Get bugs of a module.
*
@@ -394,6 +430,12 @@ class bugModel extends model
public function update($bugID)
{
$oldBug = $this->getById($bugID);
if($oldBug->lastEditedDate != $this->post->lastEditedDate)
{
dao::$errors[] = $this->lang->error->editedByOther;
return false;
}
$now = helper::now();
$bug = fixer::input('post')
->cleanInt('product,module,severity,project,story,task')
@@ -751,6 +793,155 @@ class bugModel extends model
$this->dao->update(TABLE_BUG)->data($bug)->autoCheck()->where('id')->eq((int)$bugID)->exec();
}
/**
* Link related bugs.
*
* @param int $bugID
* @access public
* @return void
*/
public function linkBugs($bugID)
{
if($this->post->bugs == false) return;
$bug = $this->getById($bugID);
$bugs2Link = $this->post->bugs;
$bugs = implode(',', $bugs2Link) . ',' . trim($bug->linkBug, ',');
$this->dao->update(TABLE_BUG)->set('linkBug')->eq(trim($bugs, ','))->where('id')->eq($bugID)->exec();
if(dao::isError()) die(js::error(dao::getError()));
$this->loadModel('action')->create('bug', $bugID, 'linkRelatedBug', '', implode(',', $bugs2Link));
}
/**
* Get bugs to link.
*
* @param int $bugID
* @param string $browseType
* @param int $queryID
* @access public
* @return array
*/
public function getBugs2Link($bugID, $browseType = 'bySearch', $queryID)
{
if($browseType == 'bySearch')
{
$bug = $this->getById($bugID);
$bugs2Link = $this->getBySearch($bug->product, $queryID, 'id', null);
foreach($bugs2Link as $key => $bug2Link)
{
if($bug2Link->id == $bugID) unset($bugs2Link[$key]);
if(in_array($bug2Link->id, explode(',', $bug->linkBug))) unset($bugs2Link[$key]);
}
return $bugs2Link;
}
else
{
return array();
}
}
/**
* Unlink related bug.
*
* @param int $bugID
* @param int $bug2Unlink
* @access public
* @return void
*/
public function unlinkBug($bugID, $bug2Unlink = 0)
{
$bug = $this->getById($bugID);
$bugs = explode(',', trim($bug->linkBug, ','));
foreach($bugs as $key => $bugId)
{
if($bugId == $bug2Unlink) unset($bugs[$key]);
}
$bugs = implode(',', $bugs);
$this->dao->update(TABLE_BUG)->set('linkBug')->eq($bugs)->where('id')->eq($bugID)->exec();
if(dao::isError()) die(js::error(dao::getError()));
$this->loadModel('action')->create('bug', $bugID, 'unlinkRelatedBug', '', $bug2Unlink);
}
/**
* Get linkBugs.
*
* @param int $bugID
* @access public
* @return array
*/
public function getLinkBugs($bugID)
{
$bug = $this->getById($bugID);
return $this->dao->select('id, title')->from(TABLE_BUG)->where('id')->in($bug->linkBug)->fetchPairs();
}
/**
* Build search form.
*
* @param int $productID
* @param array $products
* @param int $queryID
* @param string $actionURL
* @access public
* @return void
*/
public function buildSearchForm($productID, $products, $queryID, $actionURL)
{
$this->config->bug->search['actionURL'] = $actionURL;
$this->config->bug->search['queryID'] = $queryID;
$this->config->bug->search['params']['product']['values'] = array($productID => $products[$productID], 'all' => $this->lang->bug->allProduct);
$this->config->bug->search['params']['plan']['values'] = $this->loadModel('productplan')->getPairs($productID);
$this->config->bug->search['params']['module']['values'] = $this->loadModel('tree')->getOptionMenu($productID, $viewType = 'bug', $startModuleID = 0);
$this->config->bug->search['params']['project']['values'] = $this->product->getProjectPairs($productID);
$this->config->bug->search['params']['openedBuild']['values'] = $this->loadModel('build')->getProductBuildPairs($productID, 0, $params = '');
$this->config->bug->search['params']['resolvedBuild']['values'] = $this->config->bug->search['params']['openedBuild']['values'];
if($this->session->currentProductType == 'normal')
{
unset($this->config->bug->search['fields']['branch']);
unset($this->config->bug->search['params']['branch']);
}
else
{
$this->config->bug->search['fields']['branch'] = $this->lang->product->branch;
$this->config->bug->search['params']['branch']['values'] = array('' => '') + $this->loadModel('branch')->getPairs($productID, 'noempty');
}
$this->loadModel('search')->setSearchParams($this->config->bug->search);
}
/**
* Process the openedBuild and resolvedBuild fields for bugs.
*
* @param array $bugs
* @access public
* @return array
*/
public function processBuildForBugs($bugs)
{
$productIdList = array();
foreach($bugs as $bug) $productIdList[$bug->id] = $bug->product;
$builds = $this->loadModel('build')->getProductBuildPairs(array_unique($productIdList), 0, $params = '');
/* Process the openedBuild and resolvedBuild fields. */
foreach($bugs as $key => $bug)
{
$openBuildIdList = explode(',', $bug->openedBuild);
$openedBuild = '';
foreach($openBuildIdList as $buildID)
{
$openedBuild .= isset($builds[$buildID]) ? $builds[$buildID] : $buildID;
$openedBuild .= ',';
}
$bug->openedBuild = rtrim($openedBuild, ',');
$bug->resolvedBuild = isset($builds[$bug->resolvedBuild]) ? $builds[$bug->resolvedBuild] : $bug->resolvedBuild;
}
return $bugs;
}
/**
* Extract accounts from some bugs.
*
@@ -856,7 +1047,8 @@ class bugModel extends model
{
$bugs = $this->dao->select('*')->from(TABLE_BUG)
->where('deleted')->eq(0)
->andWhere("(project = '" . (int)$projectID . "' " . (empty($build) ? '' : "AND CONCAT(',', openedBuild, ',') like '%,$build,%'") . ")")
->beginIF(empty($build))->andWhere('project')->eq($projectID)->fi()
->beginIF($build)->andWhere("CONCAT(',', openedBuild, ',') like '%,$build,%'")->fi()
->orderBy($orderBy)->page($pager)->fetchAll();
$this->loadModel('common')->saveQueryCondition($this->dao->get(), 'bug');
@@ -1090,7 +1282,7 @@ class bugModel extends model
{
$datas = $this->dao->select('module as name, count(module) as value')->from(TABLE_BUG)->where($this->reportCondition())->groupBy('module')->orderBy('value DESC')->fetchAll('name');
if(!$datas) return array();
$modules = $this->dao->select('id, name')->from(TABLE_MODULE)->where('id')->in(array_keys($datas))->fetchPairs();
$modules = $this->loadModel('tree')->getModulesName(array_keys($datas));
foreach($datas as $moduleID => $data) $data->name = isset($modules[$moduleID]) ? $modules[$moduleID] : '/';
return $datas;
}
@@ -1618,7 +1810,7 @@ class bugModel extends model
$bugQuery = str_replace($allProduct, '1', $this->session->bugQuery);
$bugQuery = $bugQuery . ' AND `product`' . helper::dbIN($products);
}
if($branch) $bugQuery .= "AND `branch` in('0','$branch')";
if($branch) $bugQuery .= " AND `branch` in('0','$branch')";
$bugs = $this->dao->select('*')->from(TABLE_BUG)->where($bugQuery)
->andWhere('deleted')->eq(0)
->orderBy($orderBy)->page($pager)->fetchAll();

View File

@@ -16,8 +16,8 @@
<span class='prefix'><?php echo html::icon($lang->icons['bug']);?></span>
<strong>
<small class='text-muted'><?php echo html::icon($lang->icons['batchCreate']);?></small>
<?php if($this->session->currentProductType !== 'normal') echo '<span class="label label-info">' . $branches[$branch] . '</span>';?>
<?php echo $lang->bug->common . $lang->colon . $lang->bug->batchCreate;?>
<?php if($this->session->currentProductType !== 'normal') echo '<span class="label label-info">' . $branches[$branch] . '</span>';?>
</strong>
<div class='actions'>
<?php if(common::hasPriv('file', 'uploadImages')) echo html::a($this->createLink('file', 'uploadImages', 'module=bug&params=' . helper::safe64Encode("productID=$productID&projectID=$projectID&moduleID=$moduleID")), $lang->uploadImages, '', "data-toggle='modal' data-type='iframe' class='btn' data-width='600px'")?>
@@ -57,7 +57,7 @@
?>
<?php $i = 0; ?>
<?php if(!empty($titles)):?>
<?php foreach($titles as $fileName => $bugTitle):?>
<?php foreach($titles as $bugTitle => $fileName):?>
<?php
$moduleID = $i == 0 ? $moduleID : 'ditto';
$projectID = $i == 0 ? $projectID : 'ditto';

View File

@@ -12,7 +12,6 @@
?>
<?php
include '../../common/view/header.html.php';
include '../../common/view/treeview.html.php';
js::set('browseType', $browseType);
js::set('moduleID', $moduleID);
?>
@@ -73,14 +72,13 @@ js::set('moduleID', $moduleID);
<?php echo $moduleTree;?>
<div class='text-right'>
<?php common::printLink('tree', 'browse', "productID=$productID&view=bug", $lang->tree->manage);?>
<?php common::printLink('tree', 'fix', "root=$productID&type=bug", $lang->tree->fix, 'hiddenwin');?>
</div>
</div>
</div>
</div>
</div>
<div class='main'>
<form method='post'>
<form method='post' id='bugForm'>
<table class='table table-condensed table-hover table-striped tablesorter table-fixed' id='bugList'>
<?php $vars = "productID=$productID&branch=$branch&browseType=$browseType&param=$param&orderBy=%s&recTotal={$pager->recTotal}&recPerPage={$pager->recPerPage}"; ?>
<thead>
@@ -176,9 +174,7 @@ js::set('moduleID', $moduleID);
<td colspan='<?php echo $columns;?>'>
<?php if(!empty($bugs)):?>
<div class='table-actions clearfix'>
<div class='btn-group'>
<?php echo html::selectButton();?>
</div>
<div class='btn-group dropup'>
<?php
$actionLink = $this->createLink('bug', 'batchEdit', "productID=$productID&branch=$branch");

View File

@@ -63,7 +63,8 @@ js::set('refresh', $lang->refresh);
<span class='input-group-addon'><?php echo $lang->bug->openedBuild?></span>
<span id='buildBox'><?php echo html::select('openedBuild[]', $builds, $buildID, "size=4 multiple=multiple class='chosen form-control'");?></span>
<span class='input-group-addon' id='buildBoxActions'></span>
<span class='input-group-btn'><?php echo html::commonButton($lang->bug->allBuilds, "class='btn btn-default' onclick='loadAllBuilds()'")?></span>
<span class='input-group-btn'><?php echo html::commonButton('<i class="icon icon-refresh"></i>', "class='btn btn-default' data-toggle='tooltip' onclick='loadAllBuilds()' title='{$lang->bug->allBuilds}'")?></span>
</div>
</div>
</td>
</tr>
@@ -72,7 +73,7 @@ js::set('refresh', $lang->refresh);
<td>
<div class='input-group'>
<span id='assignedToBox'><?php echo html::select('assignedTo', $projectMembers, $assignedTo, "class='form-control chosen'");?></span>
<span class='input-group-btn'><?php echo html::commonButton($lang->bug->allUsers, "class='btn btn-default' onclick='loadAllUsers()'");?></span>
<span class='input-group-btn'><?php echo html::commonButton('<i class="icon icon-refresh"></i>', "class='btn btn-default' onclick='loadAllUsers()' data-toggle='tooltip' title='{$lang->bug->allUsers}'");?></span>
</div>
</td>
</tr>
@@ -97,14 +98,36 @@ js::set('refresh', $lang->refresh);
<tr>
<th><?php echo $lang->bug->title;?></th>
<td colspan='2'>
<div class='row'>
<div class='col-sm-8'><?php echo html::input('title', $bugTitle, "class='form-control'");?></div>
<div class='col-sm-4'>
<div class='row-table'>
<div class='col-table w-p100'><?php echo html::input('title', $bugTitle, "class='form-control'");?></div>
<div class='col-table'>
<div class='input-group'>
<span class='input-group-addon fix-border'><?php echo $lang->bug->severity?></span>
<?php echo html::select('severity', $lang->bug->severityList, $severity, "class='form-control'");?>
<span class='input-group-addon fix-border'><?php echo $lang->bug->pri?></span>
<?php echo html::select('pri', $lang->bug->priList, $severity, "class='form-control'");?>
<span class='input-group-addon fix-border br-0'><?php echo $lang->bug->severity;?></span>
<?php $isAllNumberSeverity = is_numeric(join($lang->bug->severityList));?>
<?php if(!$isAllNumberSeverity):?>
<?php echo html::select('pri', (array)$lang->bug->severityList, $severity, "class='form-control minw-80px'");?>
<?php else: ?>
<div class='input-group-btn dropdown-pris' data-prefix='severity'>
<button type='button' class='btn dropdown-toggle br-0' data-toggle='dropdown'>
<span class='pri-text'></span> &nbsp;<span class='caret'></span>
</button>
<ul class='dropdown-menu pull-right'></ul>
<?php echo html::select('severity', (array)$lang->bug->severityList, $severity, "class='hide'");?>
</div>
<?php endif; ?>
<span class='input-group-addon fix-border br-0'><?php echo $lang->bug->pri;?></span>
<?php $isAllNumberPri = is_numeric(join($lang->bug->priList));?>
<?php if(!$isAllNumberPri):?>
<?php echo html::select('pri', (array)$lang->bug->priList, '', "class='form-control minw-80px'");?>
<?php else: ?>
<div class='input-group-btn dropdown-pris'>
<button type='button' class='btn dropdown-toggle br-0' data-toggle='dropdown'>
<span class='pri-text'></span> &nbsp;<span class='caret'></span>
</button>
<ul class='dropdown-menu pull-right'></ul>
<?php echo html::select('pri', $lang->bug->priList, '', "class='hide'");?>
</div>
<?php endif; ?>
</div>
</div>
</div>
@@ -142,17 +165,15 @@ js::set('refresh', $lang->refresh);
<tr>
<th><?php echo $lang->bug->lblMailto;?></th>
<td>
<div class='input-group'>
<div class='input-group' id='contactListGroup'>
<?php
echo html::select('mailto[]', $users, str_replace(' ', '', $mailto), "class='form-control chosen' multiple");
if($contactLists) echo html::select('', $contactLists, '', "class='form-control chosen' onchange=\"setMailto('mailto', this.value)\"");
if($contactLists) echo html::select('', $contactLists, '', "class='form-control' style='min-width: 100px; margin-left: -1px' onchange=\"setMailto('mailto', this.value)\"");
if(empty($contactLists))
{
echo '<span class="input-group-addon">';
echo '<a href="' . $this->createLink('company', 'browse') . '" target="_blank">' . $lang->user->contacts->manage . '</a>';
echo '</span>';
echo '<span class="input-group-addon">';
echo '<a href="###" onclick="ajaxGetContacts(this)">' . $lang->refresh . '</a>';
echo '<span class="input-group-btn">';
echo '<a href="' . $this->createLink('company', 'browse') . '" target="_blank" data-toggle="tooltip" class="btn" title="' . $lang->user->contacts->manage . '"><i class="icon icon-cog"></i></a>';
echo '<a href="###" onclick="ajaxGetContacts(this)" data-toggle="tooltip" class="btn" title="' . $lang->refresh . '"><i class="icon icon-refresh"></i></a>';
echo '</span>';
}
?>
@@ -160,7 +181,7 @@ js::set('refresh', $lang->refresh);
</td>
<td>
<div class='input-group'>
<span class='input-group-addon'><?php echo $lang->bug->keywords;?></span>
<span class='input-group-addon' id='keywordsAddonLabel'><?php echo $lang->bug->keywords;?></span>
<?php echo html::input('keywords', $keywords, "class='form-control'");?>
</div>
</td>

View File

@@ -56,6 +56,7 @@ js::set('oldResolvedBuild' , $bug->resolvedBuild);
</fieldset>
<div class='actions'>
<?php
echo html::hidden('lastEditedDate', $bug->lastEditedDate);
echo html::submitButton();
$browseLink = $app->session->bugList != false ? $app->session->bugList : inlink('browse', "productID=$bug->product");
echo html::linkButton($lang->goback, $browseLink);
@@ -229,14 +230,32 @@ js::set('oldResolvedBuild' , $bug->resolvedBuild);
<fieldset>
<legend><?php echo $lang->bug->legendMisc;?></legend>
<table class='table table-form'>
<tr>
<tr class='text-top'>
<th class='w-80px'><?php echo $lang->bug->linkBug;?></th>
<td><?php echo html::input('linkBug', $bug->linkBug, 'class="form-control"');?></td>
<td>
<?php echo html::a($this->createLink('bug', 'linkBugs', "bugID=$bug->id", '', true), $lang->bug->linkBugs, '', "data-toggle='modal' data-type='iframe' data-width='95%'");?>
<ul class='list-unstyled' id='linkBugBox'>
<?php
if(isset($bug->linkBugTitles))
{
foreach($bug->linkBugTitles as $linkBugID => $linkBugTitle)
{
echo '<li>';
echo html::a(inlink('view', "bugID=$linkBugID"), "#$linkBugID " . $linkBugTitle, '_blank');
echo html::a("javascript:unlinkBug($bug->id, $linkBugID)", '<i class="icon-remove"></i>', '', "title='{$lang->unlink}' style='float:right'");
echo '</li>';
}
}
?>
</ul>
</td>
</tr>
<?php if($bug->case):?>
<tr>
<th><?php echo $lang->bug->case;?></th>
<th><?php echo $lang->bug->fromCase;?></th>
<td><?php echo html::input('case', $bug->case, 'class="form-control"');?></td>
</tr>
<?php endif;?>
</table>
</fieldset>
</div>

View File

@@ -0,0 +1,75 @@
<?php
/**
* The link bug view of bug module of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Fei Chen <chenfei@cnezsoft.com>
* @package bug
* @version $Id: linkbugs.html.php 4129 2016-03-08 09:00:12Z chenfei $
* @link http://www.zentao.net
*/
?>
<?php include '../../common/view/header.html.php';?>
<div class='container'>
<div id='titlebar'>
<div class='heading'>
<span class='prefix'><?php echo html::icon($lang->icons['bug']);?> <strong><?php echo $bug->id;?></strong></span>
<strong><?php echo html::a($this->createLink('bug', 'view', "bugID=$bug->id"), $bug->title, '_blank');?></strong>
<small class='text-muted'> <?php echo $lang->bug->linkBugs;?> <?php echo html::icon($lang->icons['link']);?></small>
</div>
<div id='querybox' class='show'></div>
</div>
<form method='post' class='form-condensed' target='hiddenwin' id='linkBugsForm'>
<table class='table table-condensed table-hover table-striped tablesorter' id='bugList'>
<?php if($bugs2Link):?>
<thead>
<tr>
<th class='w-id'><?php echo $lang->idAB;?></th>
<th class='w-pri'><?php echo $lang->priAB;?></th>
<th><?php echo $lang->bug->product;?></th>
<th><?php echo $lang->bug->title;?></th>
<th><?php echo $lang->bug->statusAB;?></th>
<th class='w-user'><?php echo $lang->openedByAB;?></th>
<th class='w-user'><?php echo $lang->assignedToAB;?></th>
</tr>
</thead>
<tbody>
<?php $bugCount = 0;?>
<?php foreach($bugs2Link as $bug2Link):?>
<?php $bugLink = $this->createLink('bug', 'view', "bugID=$bug2Link->id");?>
<tr class='text-center'>
<td class='text-left'>
<input type='checkbox' name='bugs[]' value='<?php echo $bug2Link->id;?>'/>
<?php echo html::a($bugLink, sprintf('%03d', $bug2Link->id));?>
</td>
<td><span class='<?php echo 'pri' . zget($lang->bug->priList, $bug2Link->pri, $bug2Link->pri)?>'><?php echo zget($lang->bug->priList, $bug2Link->pri, $bug2Link->pri);?></span></td>
<td><?php echo html::a($this->createLink('product', 'browse', "productID=$bug2Link->product&branch=$bug2Link->branch"), $products[$bug2Link->product], '_blank');?></td>
<td class='text-left nobr' title="<?php echo $bug2Link->title?>"><?php echo html::a($bugLink, $bug2Link->title);?></td>
<td><?php echo $lang->bug->statusList[$bug->status];?></td>
<td><?php echo $users[$bug2Link->openedBy];?></td>
<td><?php echo $users[$bug2Link->assignedTo];?></td>
</tr>
<?php $bugCount ++;?>
<?php endforeach;?>
</tbody>
<tfoot>
<tr>
<td colspan='7' class='text-left'>
<div class='table-actions clearfix'>
<?php if($bugCount) echo html::selectButton() . html::submitButton();?>
</div>
</td>
</tr>
<tr>
<td class='hidden'><?php echo html::input('bug', $bug->id);?></td>
</tr>
</tfoot>
<?php endif;?>
</table>
</form>
</div>
<script type='text/javascript'>
$(function(){ajaxGetSearchForm();});
</script>
<?php include '../../common/view/footer.html.php';?>

View File

@@ -31,7 +31,7 @@
<form method='post'>
<?php echo html::checkBox('charts', $lang->bug->report->charts, $checkedCharts, '', 'block');?>
<?php echo html::selectAll('', "button", false, 'btn-sm')?>
<?php echo html::submitButton($lang->bug->report->create, '', "btn-primary btn-sm");?>
<?php echo html::submitButton($lang->bug->report->create, '', "btn btn-primary btn-sm");?>
</form>
</div>
</div>

View File

@@ -10,26 +10,23 @@
* @link http://www.zentao.net
*/
?>
<?php
$onlybody = isonlybody() ? true : false;
if($onlybody) $_GET['onlybody'] = 'no';
?>
<table width='98%' align='center'>
<tr class='header'>
<td>
BUG #<?php echo $bug->id . "=>{$users[$bug->assignedTo]} " . html::a(common::getSysURL() . $this->createLink('bug', 'view', "bugID=$bug->id"), $bug->title);?>
</td>
</tr>
<tr>
<td>
<fieldset>
<legend><?php echo $lang->bug->legendSteps;?></legend>
<div class='content'><?php echo $bug->steps;?></div>
<?php $mailTitle = 'BUG #' . $bug->id . ' ' . $bug->title;?>
<?php include '../../common/view/mail.header.html.php';?>
<tr>
<td>
<table cellpadding='0' cellspacing='0' width='600' style='border: none; border-collapse: collapse;'>
<tr>
<td style='padding: 10px; background-color: #F8FAFE; border: none; font-size: 14px; font-weight: 500; border-bottom: 1px solid #e5e5e5;'><?php echo html::a(common::getSysURL() . $this->createLink('bug', 'view', "bugID=$bug->id"), $mailTitle, '', "style='color: #333'");?></td>
</tr>
</table>
</td>
</tr>
<tr>
<td style='padding: 10px; border: none;'>
<fieldset style='border: 1px solid #e5e5e5'>
<legend style='color: #114f8e'><?php echo $lang->bug->legendSteps;?></legend>
<div style='padding:5px;'><?php echo $bug->steps;?></div>
</fieldset>
</td>
</tr>
<tr>
<td><?php include '../../common/view/mail.html.php';?></td>
</tr>
</table>
<?php if($onlybody) $_GET['onlybody'] = 'yes';?>
</td>
</tr>
<?php include '../../common/view/mail.footer.html.php';?>

View File

@@ -154,11 +154,11 @@
</tr>
<tr>
<th><?php echo $lang->bug->severity;?></th>
<td><strong><?php echo zget($lang->bug->severityList, $bug->severity, $bug->severity);?></strong></td>
<td><span class='<?php echo 'severity' . zget($lang->bug->severityList, $bug->severity);?>'><?php echo zget($lang->bug->severityList, $bug->severity)?></span></td>
</tr>
<tr>
<th><?php echo $lang->bug->pri;?></th>
<td><strong><?php echo $lang->bug->priList[$bug->pri];?></strong></td>
<td><span class='<?php echo 'pri' . zget($lang->bug->priList, $bug->pri);?>'><?php echo zget($lang->bug->priList, $bug->pri)?></span></td>
</tr>
<tr>
<th><?php echo $lang->bug->status;?></th>
@@ -279,23 +279,8 @@
</div>
<div class='tab-pane' id='legendMisc'>
<table class='table table-data table-condensed table-borderless table-fixed'>
<tr>
<th class='w-60px'><?php echo $lang->bug->fromCase;?></th>
<td><?php if($bug->case) echo html::a($this->createLink('testcase', 'view', "caseID=$bug->case"), $bug->caseTitle);?></td>
</tr>
<tr>
<th><?php echo $lang->bug->toCase;?></th>
<td>
<?php
foreach($bug->toCases as $caseID => $case)
{
echo '<p style="margin-bottom:0;">' . html::a($this->createLink('testcase', 'view', "caseID=$caseID"), $case) . '</p>';
}
?>
</td>
</tr>
<tr>
<th><?php echo $lang->bug->linkBug;?></th>
<tr class='text-top'>
<th class='w-60px'><?php echo $lang->bug->linkBug;?></th>
<td>
<?php
if(isset($bug->linkBugTitles))
@@ -308,18 +293,37 @@
?>
</td>
</tr>
<?php if($bug->case):?>
<tr>
<th><?php echo $lang->bug->case;?></th>
<td><?php if(isset($bug->caseTitle)) echo html::a($this->createLink('testcase', 'view', "caseID=$bug->case"), "#$bug->case $bug->caseTitle", '_blank');?></td>
<th class='w-60px'><?php echo $lang->bug->fromCase;?></th>
<td><?php echo html::a($this->createLink('testcase', 'view', "caseID=$bug->case"), "#$bug->case $bug->caseTitle", '_blank');?></td>
</tr>
<?php endif;?>
<?php if($bug->toCases):?>
<tr>
<th><?php echo $lang->bug->toCase;?></th>
<td>
<?php
foreach($bug->toCases as $caseID => $case)
{
echo '<p style="margin-bottom:0;">' . html::a($this->createLink('testcase', 'view', "caseID=$caseID"), $case) . '</p>';
}
?>
</td>
</tr>
<?php endif;?>
<?php if($bug->toStory != 0):?>
<tr>
<th><?php echo $lang->bug->toStory;?></th>
<td><?php if($bug->toStory != 0) echo html::a($this->createLink('story', 'view', "storyID=$bug->toStory"), "#$bug->toStory $bug->toStoryTitle", '_blank');?></td>
<td><?php echo html::a($this->createLink('story', 'view', "storyID=$bug->toStory"), "#$bug->toStory $bug->toStoryTitle", '_blank');?></td>
</tr>
<?php endif;?>
<?php if($bug->toTask != 0):?>
<tr>
<th><?php echo $lang->bug->toTask;?></th>
<td><?php if($bug->toTask != 0) echo html::a($this->createLink('task', 'view', "taskID=$bug->toTask"), "#$bug->toTask $bug->toTaskTitle", '_blank');?></td>
<td><?php echo html::a($this->createLink('task', 'view', "taskID=$bug->toTask"), "#$bug->toTask $bug->toTaskTitle", '_blank');?></td>
</tr>
<?php endif;?>
</table>
</div>
</div>

View File

@@ -213,6 +213,7 @@ class build extends control
*/
public function ajaxGetProductBuilds($productID, $varName, $build = '', $branch = 0, $index = 0, $type = 'normal')
{
$branch = $branch ? "0,$branch" : $branch;
if($varName == 'openedBuild' )
{
$params = ($type == 'all') ? 'noempty' : 'noempty, noterminate, nodone';
@@ -241,7 +242,8 @@ class build extends control
*/
public function ajaxGetProjectBuilds($projectID, $productID, $varName, $build = '', $branch = 0, $index = 0, $needCreate = false, $type = 'normal')
{
if($varName == 'openedBuild')
$branch = $branch ? "0,$branch" : $branch;
if($varName == 'openedBuild')
{
$params = ($type == 'all') ? 'noempty' : 'noempty, noterminate, nodone';
$builds = $this->build->getProjectBuildPairs($projectID, $productID, $branch, $params);
@@ -273,7 +275,9 @@ class build extends control
die(js::locate(inlink('view', "buildID=$buildID&type=story"), 'parent'));
}
$build = $this->build->getById($buildID);
$this->session->set('storyList', inlink('view', "buildID=$buildID&type=story&link=true&param=" . helper::safe64Encode("&browseType=$browseType&queryID=$param")));
$build = $this->build->getById($buildID);
$product = $this->loadModel('product')->getById($build->product);
$this->loadModel('project')->setMenu($this->project->getPairs(), $build->project);
$this->loadModel('story');
$this->loadModel('tree');
@@ -289,8 +293,18 @@ class build extends control
$this->config->product->search['params']['plan']['values'] = $this->loadModel('productplan')->getForProducts(array($build->product => $build->product));
$this->config->product->search['params']['module']['values'] = $this->tree->getOptionMenu($build->product, $viewType = 'story', $startModuleID = 0);
$this->config->product->search['params']['status'] = array('operator' => '=', 'control' => 'select', 'values' => $this->lang->story->statusList);
unset($this->config->product->search['fields']['branch']);
unset($this->config->product->search['params']['branch']);
if($product->type == 'normal')
{
unset($this->config->product->search['fields']['branch']);
unset($this->config->product->search['params']['branch']);
}
else
{
$this->config->product->search['fields']['branch'] = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
$branches = array('' => '') + $this->loadModel('branch')->getPairs($build->product, 'noempty');
if($build->branch) $branches = array('' => '', $build->branch => $branches[$build->branch]);
$this->config->product->search['params']['branch']['values'] = $branches;
}
$this->loadModel('search')->setSearchParams($this->config->product->search);
if($browseType == 'bySearch')
@@ -371,8 +385,10 @@ class build extends control
die(js::locate(inlink('view', "buildID=$buildID&type=bug"), 'parent'));
}
$this->session->set('bugList', inlink('view', "buildID=$buildID&type=bug&link=true&param=" . helper::safe64Encode("&browseType=$browseType&queryID=$param")));
/* Set menu. */
$build = $this->build->getByID($buildID);
$build = $this->build->getByID($buildID);
$product = $this->loadModel('product')->getByID($build->product);
$this->loadModel('project')->setMenu($this->project->getPairs(), $build->project);
$queryID = ($browseType == 'bysearch') ? (int)$param : 0;
@@ -387,8 +403,21 @@ class build extends control
$this->config->bug->search['params']['project']['values'] = $this->loadModel('product')->getProjectPairs($build->product);
$this->config->bug->search['params']['openedBuild']['values'] = $this->build->getProductBuildPairs($build->product, $branch = 0, $params = '');
$this->config->bug->search['params']['resolvedBuild']['values'] = $this->config->bug->search['params']['openedBuild']['values'];
unset($this->config->bug->search['fields']['branch']);
unset($this->config->bug->search['params']['branch']);
unset($this->config->bug->search['fields']['product']);
unset($this->config->bug->search['params']['product']);
if($product->type == 'normal')
{
unset($this->config->bug->search['fields']['branch']);
unset($this->config->bug->search['params']['branch']);
}
else
{
$this->config->bug->search['fields']['branch'] = sprintf($this->lang->product->branch, $this->lang->product->branchName[$product->type]);
$branches = array('' => '') + $this->loadModel('branch')->getPairs($build->product, 'noempty');
if($build->branch) $branches = array('' => '', $build->branch => $branches[$build->branch]);
$this->config->bug->search['params']['branch']['values'] = $branches;
}
$this->loadModel('search')->setSearchParams($this->config->bug->search);
if($browseType == 'bySearch')

View File

@@ -3,3 +3,4 @@
.red {color:red}
.green {color:green}
input[type=checkbox].ml-10px{margin-left:10px;}
.pdl-8px{padding-left:8px;}

View File

@@ -1,5 +1,11 @@
.tabs{position:relative;}
.tabs .tab-content{padding:0px;border:0px;}
.tabs .tab-content .tab-pane .action{position: absolute; right: 0px; top: 0px;}
.tabs .tab-content .tab-pane #querybox{margin:0px; border-right:1px solid #ddd; border-left:1px solid #ddd;}
.tabs .tab-content .tab-pane #querybox form{padding-left:0px;}
.tabs {position:relative;}
.tabs .nav-tabs{border-bottom:none;}
.tabs .nav-tabs>li{margin-bottom:0px;}
.tabs .tab-content {padding: 0; border: none;}
.tabs .tab-content .tab-pane .action {position: absolute; right: 0; top: -1px;}
.tabs .tab-content .tab-pane #querybox{margin:0px; border-left:1px solid #ddd; border-right:1px solid #ddd;}
.tabs .tab-content .tab-pane #querybox form {padding-left:0px;}
.table-actions {padding-left: 8px;}

View File

@@ -8,10 +8,21 @@ function showLink(buildID, type, param)
$('#' + type + 'List').hide();
var formID = type == 'story' ? '#unlinkedStoriesForm' : '#unlinkedBugsForm';
fixTfootAction(formID);
setTimeout(function(){fixedTfootAction(formID)}, 100);
autoCheck();
});
}
$(function()
{
if(link == 'true') showLink(buildID, type, param);
fixedTfootAction($('#' + type + 'List').closest('form'));
$('.nav.nav-tabs a[data-toggle="tab"]').on('shown.zui.tab', function(e)
{
var href = $(e.target).attr('href');
var tabPane = $(href + '.tab-pane');
if(tabPane.size() == 0) return;
var formID = tabPane.find('.linkBox').find('form:last');
if(formID.size() == 0) formID = tabPane.find('form:last');
setTimeout(function(){fixedTfootAction(formID)}, 100);
});
})

View File

@@ -17,8 +17,6 @@ $lang->build->linkBug = "Link Bug";
$lang->build->delete = "Delete";
$lang->build->deleted = "Deleted";
$lang->build->view = "Info";
$lang->build->ajaxGetProductBuilds = "API:{$lang->productCommon} builds";
$lang->build->ajaxGetProjectBuilds = "API:{$lang->projectCommon} builds";
$lang->build->batchUnlink = 'Batch unlink';
$lang->build->batchUnlinkStory = 'Batch unlink story';
$lang->build->batchUnlinkBug = 'Batch unlink bug';
@@ -31,7 +29,6 @@ $lang->build->basicInfo = 'Basic Info';
$lang->build->id = 'ID';
$lang->build->product = $lang->productCommon;
$lang->build->project = $lang->projectCommon;
$lang->build->name = 'Name';
$lang->build->date = 'Build date';
$lang->build->builder = 'Builder';
@@ -40,10 +37,7 @@ $lang->build->filePath = 'Package file path';
$lang->build->desc = 'Desc';
$lang->build->files = 'Upload package';
$lang->build->last = 'Last build';
$lang->build->linkStoriesAndBugs = 'stories and bugs';
$lang->build->linkStories = 'Stories';
$lang->build->unlinkStory = 'Remove story';
$lang->build->linkBugs = 'Bugs';
$lang->build->unlinkBug = 'Remove bug';
$lang->build->stories = 'Finished stories';
$lang->build->bugs = 'Resolved bugs';

View File

@@ -17,8 +17,6 @@ $lang->build->linkBug = "关联Bug";
$lang->build->delete = "删除版本";
$lang->build->deleted = "已删除";
$lang->build->view = "版本详情";
$lang->build->ajaxGetProductBuilds = "接口:{$lang->productCommon}版本列表";
$lang->build->ajaxGetProjectBuilds = "接口:{$lang->projectCommon}版本列表";
$lang->build->batchUnlink = '批量移除';
$lang->build->batchUnlinkStory = '批量移除需求';
$lang->build->batchUnlinkBug = '批量移除Bug';
@@ -31,7 +29,6 @@ $lang->build->basicInfo = '基本信息';
$lang->build->id = 'ID';
$lang->build->product = $lang->productCommon;
$lang->build->project = $lang->projectCommon;
$lang->build->name = '名称编号';
$lang->build->date = '打包日期';
$lang->build->builder = '构建者';
@@ -40,10 +37,7 @@ $lang->build->filePath = '下载地址';
$lang->build->desc = '描述';
$lang->build->files = '上传发行包';
$lang->build->last = '上个版本';
$lang->build->linkStoriesAndBugs = '关联需求和Bug';
$lang->build->linkStories = '相关需求';
$lang->build->unlinkStory = '移除需求';
$lang->build->linkBugs = '相关Bug';
$lang->build->unlinkBug = '移除Bug';
$lang->build->stories = '完成的需求';
$lang->build->bugs = '解决的Bug';

View File

@@ -17,8 +17,6 @@ $lang->build->linkBug = "關聯Bug";
$lang->build->delete = "刪除版本";
$lang->build->deleted = "已刪除";
$lang->build->view = "版本詳情";
$lang->build->ajaxGetProductBuilds = "介面:{$lang->productCommon}版本列表";
$lang->build->ajaxGetProjectBuilds = "介面:{$lang->projectCommon}版本列表";
$lang->build->batchUnlink = '批量移除';
$lang->build->batchUnlinkStory = '批量移除需求';
$lang->build->batchUnlinkBug = '批量移除Bug';
@@ -31,7 +29,6 @@ $lang->build->basicInfo = '基本信息';
$lang->build->id = 'ID';
$lang->build->product = $lang->productCommon;
$lang->build->project = $lang->projectCommon;
$lang->build->name = '名稱編號';
$lang->build->date = '打包日期';
$lang->build->builder = '構建者';
@@ -40,10 +37,7 @@ $lang->build->filePath = '下載地址';
$lang->build->desc = '描述';
$lang->build->files = '上傳發行包';
$lang->build->last = '上個版本';
$lang->build->linkStoriesAndBugs = '關聯需求和Bug';
$lang->build->linkStories = '相關需求';
$lang->build->unlinkStory = '移除需求';
$lang->build->linkBugs = '相關Bug';
$lang->build->unlinkBug = '移除Bug';
$lang->build->stories = '完成的需求';
$lang->build->bugs = '解決的Bug';

View File

@@ -71,21 +71,24 @@ class buildModel extends model
if(strpos($params, 'noempty') === false) $sysBuilds = array('' => '');
if(strpos($params, 'notrunk') === false) $sysBuilds = $sysBuilds + array('trunk' => 'Trunk');
$projectBuilds = $this->dao->select('t1.id, t1.name, t4.name as branchName')->from(TABLE_BUILD)->alias('t1')
$projectBuilds = $this->dao->select('t1.id, t1.name, t1.project, t2.status as projectStatus, t3.id as releaseID, t3.status as releaseStatus, t4.name as branchName')->from(TABLE_BUILD)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
->leftJoin(TABLE_RELEASE)->alias('t3')->on('t1.id = t3.build')
->leftJoin(TABLE_BRANCH)->alias('t4')->on('t1.branch = t4.id')
->where('t1.project')->eq((int)$projectID)
->beginIF($productID)->andWhere('t1.product')->eq((int)$productID)->fi()
->beginIF($branch)->andWhere('t1.branch')->eq($branch)->fi()
->beginIF(strpos($params, 'nodone') !== false)->andWhere('t2.status')->ne('done')->fi()
->beginIF(strpos($params, 'noterminate') !== false)->andWhere('t3.status')->ne('terminate')->fi()
->beginIF($branch)->andWhere('t1.branch')->in($branch)->fi()
->andWhere('t1.deleted')->eq(0)
->orderBy('t1.date desc, t1.id desc')->fetchAll('id');
/* Set builds and filter terminate releases. */
$builds = array();
foreach($projectBuilds as $buildID => $build) $builds[$buildID] = $build->name;
foreach($projectBuilds as $buildID => $build)
{
if(empty($build->releaseID) and (strpos($params, 'nodone') !== false) and ($build->projectStatus === 'done')) continue;
if((strpos($params, 'noterminate') !== false) and ($build->releaseStatus === 'terminate')) continue;
$builds[$buildID] = $build->name;
}
if(!$builds) return $sysBuilds;
/* if the build has been released, replace build name with release name. */
@@ -113,20 +116,24 @@ class buildModel extends model
if(strpos($params, 'noempty') === false) $sysBuilds = array('' => '');
if(strpos($params, 'notrunk') === false) $sysBuilds = $sysBuilds + array('trunk' => 'Trunk');
$productBuilds = $this->dao->select('t1.id, t1.name, t1.project, t4.name as branchName')->from(TABLE_BUILD)->alias('t1')
$productBuilds = $this->dao->select('t1.id, t1.name, t1.project, t2.status as projectStatus, t3.id as releaseID, t3.status as releaseStatus, t4.name as branchName')->from(TABLE_BUILD)->alias('t1')
->leftJoin(TABLE_PROJECT)->alias('t2')->on('t1.project = t2.id')
->leftJoin(TABLE_RELEASE)->alias('t3')->on('t1.id = t3.build')
->leftJoin(TABLE_BRANCH)->alias('t4')->on('t1.branch = t4.id')
->where('t1.product')->in($products)
->beginIF($branch)->andWhere('t1.branch')->eq($branch)->fi()
->beginIF(strpos($params, 'nodone') !== false)->andWhere('t2.status')->ne('none')->fi()
->beginIF(strpos($params, 'noterminate') !== false)->andWhere('t3.status')->ne('terminate')->fi()
->beginIF($branch)->andWhere('t1.branch')->in($branch)->fi()
->andWhere('t1.deleted')->eq(0)
->orderBy('t1.date desc, t1.id desc')->fetchAll('id');
/* Set builds and filter done projects and terminate releases. */
$builds = array();
foreach($productBuilds as $key => $build) $builds[$key] = ((strpos($params, 'withbranch') !== false and $build->branchName) ? $build->branchName . '/' : '') . $build->name;
foreach($productBuilds as $key => $build)
{
if(empty($build->releaseID) and (strpos($params, 'nodone') !== false) and ($build->projectStatus === 'done')) continue;
if((strpos($params, 'noterminate') !== false) and ($build->releaseStatus === 'terminate')) continue;
$builds[$key] = ((strpos($params, 'withbranch') !== false and $build->branchName) ? $build->branchName . '/' : '') . $build->name;
}
if(!$builds) return $sysBuilds;
/* if the build has been released and replace is true, replace build name with release name. */
@@ -174,13 +181,18 @@ class buildModel extends model
$build = fixer::input('post')
->setDefault('product', 0)
->setDefault('branch', 0)
->add('project', (int)$projectID)
->stripTags($this->config->build->editor->create['id'], $this->config->allowedTags)
->remove('resolvedBy,allchecker,files,labels')
->get();
$build = $this->loadModel('file')->processEditor($build, $this->config->build->editor->create['id']);
$this->dao->insert(TABLE_BUILD)->data($build)->autoCheck()->batchCheck($this->config->build->create->requiredFields, 'notempty')->check('name', 'unique', "product = {$build->product} AND deleted = '0'")->exec();
$this->dao->insert(TABLE_BUILD)->data($build)
->autoCheck()
->batchCheck($this->config->build->create->requiredFields, 'notempty')
->check('name', 'unique', "product = {$build->product} AND branch = {$build->branch} AND deleted = '0'")
->exec();
if(!dao::isError())
{
$buildID = $this->dao->lastInsertID();
@@ -199,7 +211,7 @@ class buildModel extends model
public function update($buildID)
{
$oldBuild = $this->getByID($buildID);
$build = fixer::input('post')->stripTags($this->config->build->editor->edit['id'], $this->config->allowedTags)
$build = fixer::input('post')->setDefault('branch', $oldBuild->branch)->stripTags($this->config->build->editor->edit['id'], $this->config->allowedTags)
->remove('allchecker,resolvedBy,files,labels')
->get();
@@ -208,8 +220,9 @@ class buildModel extends model
->autoCheck()
->batchCheck($this->config->build->edit->requiredFields, 'notempty')
->where('id')->eq((int)$buildID)
->check('name', 'unique', "id != $buildID AND product = {$build->product} AND deleted = '0'")
->check('name', 'unique', "id != $buildID AND product = {$build->product} AND branch = {$build->branch} AND deleted = '0'")
->exec();
if(isset($build->branch) and $oldBuild->branch != $build->branch) $this->dao->update(TABLE_RELEASE)->set('branch')->eq($build->branch)->where('build')->eq($buildID)->exec();
if(!dao::isError()) return common::createChanges($oldBuild, $build);
}

View File

@@ -47,8 +47,9 @@
<?php if(count($allBugs))
{
echo "<div class='table-actions clearfix'>";
echo "<div class='btn-group'>" . html::selectAll('unlinkedBugsForm') . html::selectReverse('unlinkedBugsForm') . '</div>';
echo html::submitButton($lang->build->linkBug) . html::a(inlink('view', "buildID={$build->id}&type=bug"), $lang->goback, '', "class='btn'") . '</div>';
echo html::selectButton() . html::submitButton($lang->build->linkBug);
echo html::a(inlink('view', "buildID={$build->id}&type=bug"), $lang->goback, '', "class='btn'");
echo '</div>';
}
?>
</td>

View File

@@ -48,11 +48,13 @@
<tfoot>
<tr>
<td colspan='8' class='text-left'>
<?php if(count($allStories))
<?php
if(count($allStories))
{
echo "<div class='table-actions clearfix'>";
echo "<div class='btn-group'>" . html::selectAll('unlinkedStoriesForm') . html::selectReverse('unlinkedStoriesForm') . '</div>';
echo html::submitButton($lang->story->linkStory) . html::a(inlink('view', "buildID={$build->id}&type=story"), $lang->goback, '', "class='btn'") . '</div>';
echo html::selectButton() . html::submitButton($lang->story->linkStory);
echo html::a(inlink('view', "buildID={$build->id}&type=story"), $lang->goback, '', "class='btn'");
echo '</div>';
}
?>
</td>

View File

@@ -83,7 +83,7 @@
<td class='text-left nobr'><?php echo html::a($storyLink,$story->title, '', "class='preview'");?></td>
<td><?php echo $users[$story->openedBy];?></td>
<td><?php echo $story->estimate;?></td>
<td class='<?php echo $story->status;?>'><?php echo $lang->story->statusList[$story->status];?></td>
<td class='story-<?php echo $story->status;?>'><?php echo $lang->story->statusList[$story->status];?></td>
<td><?php echo $lang->story->stageList[$story->stage];?></td>
<td>
<?php
@@ -100,7 +100,7 @@
<tr>
<td colspan='8'>
<div class='table-actions clearfix'>
<?php if($countStories and $canBatchUnlink) echo "<div class='table-actions clearfix'><div class='btn-group'>" . html::selectAll('linkedStoriesForm') . html::selectReverse('linkedStoriesForm') . '</div>' . html::submitButton($lang->build->batchUnlink) . '</div>';?>
<?php if($countStories and $canBatchUnlink) echo html::selectButton() . html::submitButton($lang->build->batchUnlink);?>
<div class='text'><?php echo sprintf($lang->build->finishStories, $countStories);?></div>
</div>
</td>
@@ -158,7 +158,7 @@
<tr>
<td colspan='8'>
<div class='table-actions clearfix'>
<?php if($countBugs and $canBatchUnlink) echo "<div class='table-actions clearfix'><div class='btn-group'>" . html::selectAll('linkedBugsForm') . html::selectReverse('linkedBugsForm') . '</div>' . html::submitButton($lang->build->batchUnlink) . '</div>';?>
<?php if($countBugs and $canBatchUnlink) echo html::selectButton() . html::submitButton($lang->build->batchUnlink);?>
<div class='text'><?php echo sprintf($lang->build->resolvedBugs, $countBugs);?></div>
</div>
</td>

View File

@@ -1,462 +0,0 @@
<?php
/**
* The control file of common module of ZenTaoPMS.
*
* @copyright Copyright 2009-2015 青岛易软天创网络科技有限公司(QingDao Nature Easy Soft Network Technology Co,LTD, www.cnezsoft.com)
* @license ZPL (http://zpl.pub/page/zplv12.html)
* @author Chunsheng Wang <chunsheng@cnezsoft.com>
* @package common
* @version $Id: control.php 5036 2013-07-06 05:26:44Z wyd621@gmail.com $
* @link http://www.zentao.net
*/
class common extends control
{
/**
* The construc method, to do some auto things.
*
* @access public
* @return void
*/
public function __construct()
{
parent::__construct();
$this->common->startSession();
$this->common->sendHeader();
$this->common->setCompany();
$this->common->setUser();
$this->common->loadConfigFromDB();
if(isset($this->config->custom->productproject))
{
$productCommon = $projectCommon = 0;
list($productCommon, $projectCommon) = explode('_', $this->config->custom->productproject);
if($productCommon != 0 or $projectCommon != 0)
{
$this->lang->productCommon = $this->lang->productCommonList[$productCommon];
$this->lang->projectCommon = $this->lang->projectCommonList[$projectCommon];
$this->app->loadLang('common');
}
}
$this->common->loadCustomFromDB();
if($this->app->getViewType() == 'mhtml') $this->common->setMobileMenu();
$this->app->loadLang('company');
}
/**
* Check upgrade's status file is ok or not.
*
* @access public
* @return void
*/
public function checkUpgradeStatus()
{
$statusFile = $this->loadModel('upgrade')->checkSafeFile();
if($statusFile)
{
echo "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /></head><body>";
echo "<table align='center' style='margin-top:100px; border:1px solid gray; font-size:14px;'><tr><td>";
printf($this->lang->upgrade->setStatusFile, $statusFile, $statusFile, $statusFile);
die('</td></tr></table></body></html>');
}
}
/**
* Check the user has permission to access this method, if not, locate to the login page or deny page.
*
* @access public
* @return void
*/
public function checkPriv()
{
$module = $this->app->getModuleName();
$method = $this->app->getMethodName();
if($this->common->isOpenMethod($module, $method)) return true;
if(!$this->loadModel('user')->isLogon() and $this->server->php_auth_user) $this->user->identifyByPhpAuth();
if(!$this->loadModel('user')->isLogon() and $this->cookie->za) $this->user->identifyByCookie();
if(isset($this->app->user))
{
if(!common::hasPriv($module, $method)) $this->common->deny($module, $method);
}
else
{
$referer = helper::safe64Encode($this->app->getURI(true));
$this->locate($this->createLink('user', 'login', "referer=$referer"));
}
}
/**
* Check the user has permisson of one method of one module.
*
* @param string $module
* @param string $method
* @static
* @access public
* @return bool
*/
public static function hasPriv($module, $method)
{
global $app, $lang;
/* Check is the super admin or not. */
$account = ',' . $app->user->account . ',';
if(strpos($app->company->admins, $account) !== false) return true;
/* If not super admin, check the rights. */
$rights = $app->user->rights['rights'];
$acls = $app->user->rights['acls'];
$module = strtolower($module);
$method = strtolower($method);
if(isset($rights[$module][$method]))
{
if(empty($acls['views'])) return true;
$menu = isset($lang->menugroup->$module) ? $lang->menugroup->$module : $module;
$menu = strtolower($menu);
if($menu != 'qa' and !isset($lang->$menu->menu)) return true;
if($menu == 'my' or $menu == 'index' or $module == 'tree') return true;
if($module == 'company' and $method == 'dynamic') return true;
if($module == 'action' and $method == 'editcomment') return true;
if(!isset($acls['views'][$menu])) return false;
return true;
}
return false;
}
/**
* Replace the %s of one key of a menu by $params.
*
* All the menus are defined in the common's language file. But there're many dynamic params, so in the defination,
* we used %s as placeholder. These %s should be setted in one module.
*
* The items of one module's menu may be an string or array. For example, please see module/common/lang.
*
* @param string $object the menus of one module
* @param string $key the menu item to be replaced
* @param string $params the params passed to the menu item
* @access public
* @return void
*/
public static function setMenuVars($menu, $key, $params)
{
if(is_array($params))
{
if(is_array($menu->$key))
{
$menu->$key = (object)$menu->$key;
$menu->$key->link = vsprintf($menu->$key->link, $params);
$menu->$key = (array)$menu->$key;
}
else
{
$menu->$key = vsprintf($menu->$key, $params);
}
}
else
{
if(is_array($menu->$key))
{
$menu->$key = (object)$menu->$key;
$menu->$key->link = sprintf($menu->$key->link, $params);
$menu->$key = (array)$menu->$key;
}
else
{
$menu->$key = sprintf($menu->$key, $params);
}
}
}
/**
* Print the link contains orderBy field.
*
* This method will auto set the orderby param according the params. Fox example, if the order by is desc,
* will be changed to asc.
*
* @param string $fieldName the field name to sort by
* @param string $orderBy the order by string
* @param string $vars the vars to be passed
* @param string $label the label of the link
* @param string $module the module name
* @param string $method the method name
* @static
* @access public
* @return void
*/
public static function printOrderLink($fieldName, $orderBy, $vars, $label, $module = '', $method = '')
{
global $lang, $app;
if(empty($module)) $module = $app->getModuleName();
if(empty($method)) $method = $app->getMethodName();
$className = 'header';
$order = explode('_', $orderBy);
$order[0] = trim($order[0], '`');
if($order[0] == $fieldName)
{
if(isset($order[1]) and $order[1] == 'asc')
{
$orderBy = "{$order[0]}_desc";
$className = 'headerSortDown';
}
else
{
$orderBy = "{$order[0]}_asc";
$className = 'headerSortUp';
}
}
else
{
$orderBy = "" . trim($fieldName, '`') . "" . '_' . 'asc';
$className = 'header';
}
$link = helper::createLink($module, $method, sprintf($vars, $orderBy));
echo "<div class='$className'>" . html::a($link, $label) . '</div>';
}
/**
* Print link to an modules' methd.
*
* Before printing, check the privilege first. If no privilege, return fasle. Else, print the link, return true.
*
* @param string $module the module name
* @param string $method the method
* @param string $vars vars to be passed
* @param string $label the label of the link
* @param string $target the target of the link
* @param string $misc others
* @param bool $newline
* @static
* @access public
* @return bool
*/
public static function printLink($module, $method, $vars = '', $label, $target = '', $misc = '', $newline = true, $onlyBody = false)
{
if(!common::hasPriv($module, $method)) return false;
echo html::a(helper::createLink($module, $method, $vars, '', $onlyBody), $label, $target, $misc, $newline);
return true;
}
/**
* Print icon of split line.
*
* @static
* @access public
* @return void
*/
public static function printDivider()
{
echo "&nbsp;&nbsp;&nbsp;&nbsp;";
}
/**
* Print icon of comment.
*
* @param string $module
* @static
* @access public
* @return void
*/
public static function printCommentIcon($module)
{
if(isonlybody()) return false;
global $lang;
if(!common::hasPriv($module, 'edit')) return false;
echo html::a('#commentBox', '<i class="icon-comment-alt"></i>', '', "title='$lang->comment' onclick='setComment()' class='btn'");
}
/**
* Print link icon.
*
* @param string $module
* @param string $method
* @param string $vars
* @param object $object
* @param string $type button|list
* @param string $icon
* @param string $target
* @param string $extraClass
* @param bool $onlyBody
* @param string $misc
* @static
* @access public
* @return void
*/
public static function printIcon($module, $method, $vars = '', $object = '', $type = 'button', $icon = '', $target = '', $extraClass = '', $onlyBody = false, $misc = '')
{
if(isonlybody() and strpos($extraClass, 'showinonlybody') === false) return false;
global $app, $lang;
/* Judge the $method of $module clickable or not, default is clickable. */
$clickable = true;
if(is_object($object))
{
if($app->getModuleName() != $module) $app->control->loadModel($module);
$modelClass = class_exists("ext{$module}Model") ? "ext{$module}Model" : $module . "Model";
if(class_exists($modelClass) and is_callable(array($modelClass, 'isClickable')))
{
$clickable = call_user_func_array(array($modelClass, 'isClickable'), array('object' => $object, 'method' => $method));
}
}
/* Set module and method, then create link to it. */
if(strtolower($module) == 'story' and strtolower($method) == 'createcase') ($module = 'testcase') and ($method = 'create');
if(strtolower($module) == 'bug' and strtolower($method) == 'tostory') ($module = 'story') and ($method = 'create');
if(strtolower($module) == 'bug' and strtolower($method) == 'createcase') ($module = 'testcase') and ($method = 'create');
if(!common::hasPriv($module, $method)) return false;
$link = helper::createLink($module, $method, $vars, '', $onlyBody);
/* Set the icon title, try search the $method defination in $module's lang or $common's lang. */
$title = $method;
if($method == 'create' and $icon == 'copy') $method = 'copy';
if(isset($lang->$method) and is_string($lang->$method)) $title = $lang->$method;
if((isset($lang->$module->$method) or $app->loadLang($module)) and isset($lang->$module->$method))
{
$title = $method == 'report' ? $lang->$module->$method->common : $lang->$module->$method;
}
if($icon == 'toStory') $title = $lang->bug->toStory;
if($icon == 'createBug') $title = $lang->testtask->createBug;
/* set the class. */
if(!$icon)
{
$icon = $lang->icons[$method] ? $lang->icons[$method] : $method;
}
if(strpos(',edit,copy,report,export,delete,', ",$method,") !== false) $module = 'common';
$class = "icon-$module-$method";
if(!$clickable) $class .= ' disabled';
if($icon) $class .= ' icon-' . $icon;
/* Create the icon link. */
if($clickable)
{
if($app->getViewType() == 'mhtml')
{
echo html::a($link, $title, $target, "class='$extraClass' data-role='button' data-mini='true' data-inline='true' data-theme='b'", true);
return;
}
if($type == 'button')
{
if($method != 'edit' and $method != 'copy' and $method != 'delete')
{
echo html::a($link, "<i class='$class'></i> " . $title, $target, "class='btn $extraClass' $misc", true);
}
else
{
echo html::a($link, "<i class='$class'></i>", $target, "class='btn $extraClass' title='$title' $misc", false);
}
}
else
{
echo html::a($link, "<i class='$class'></i>", $target, "class='btn-icon $extraClass' title='$title' $misc", false);
}
}
else
{
if($type == 'list')
{
echo "<button type='button' class='disabled btn-icon $extraClass'><i class='$class' title='$title' $misc></i></button>";
}
}
}
/**
* Print backLink and preLink and nextLink.
*
* @param string $backLink
* @param object $preAndNext
* @access public
* @return void
*/
static public function printRPN($backLink, $preAndNext = '', $linkTemplate = '')
{
global $lang, $app;
if(isonlybody()) return false;
echo html::a($backLink, '<i class="icon-goback icon-level-up icon-large icon-rotate-270"></i>', '', "class='btn' title={$lang->goback}");
if(isset($preAndNext->pre) and $preAndNext->pre)
{
$id = (isset($_SESSION['testcaseOnlyCondition']) and !$_SESSION['testcaseOnlyCondition'] and $app->getModuleName() == 'testcase' and isset($preAndNext->pre->case)) ? 'case' : 'id';
$title = isset($preAndNext->pre->title) ? $preAndNext->pre->title : $preAndNext->pre->name;
$title = '#' . $preAndNext->pre->$id . ' ' . $title;
$link = $linkTemplate ? sprintf($linkTemplate, $preAndNext->pre->$id) : inLink('view', "ID={$preAndNext->pre->$id}");
echo html::a($link, '<i class="icon-pre icon-chevron-left"></i>', '', "id='pre' class='btn' title='{$title}'");
}
if(isset($preAndNext->next) and $preAndNext->next)
{
$id = (isset($_SESSION['testcaseOnlyCondition']) and !$_SESSION['testcaseOnlyCondition'] and $app->getModuleName() == 'testcase' and isset($preAndNext->next->case)) ? 'case' : 'id';
$title = isset($preAndNext->next->title) ? $preAndNext->next->title : $preAndNext->next->name;
$title = '#' . $preAndNext->next->$id . ' ' . $title;
$link = $linkTemplate ? sprintf($linkTemplate, $preAndNext->next->$id) : inLink('view', "ID={$preAndNext->next->$id}");
echo html::a($link, '<i class="icon-pre icon-chevron-right"></i>', '', "id='next' class='btn' title='$title'");
}
}
/**
* Create changes of one object.
*
* @param mixed $old the old object
* @param mixed $new the new object
* @static
* @access public
* @return array
*/
public static function createChanges($old, $new)
{
global $config;
$changes = array();
$magicQuote = get_magic_quotes_gpc();
foreach($new as $key => $value)
{
if(strtolower($key) == 'lastediteddate') continue;
if(strtolower($key) == 'lasteditedby') continue;
if(strtolower($key) == 'assigneddate') continue;
if(strtolower($key) == 'editedby') continue;
if(strtolower($key) == 'editeddate') continue;
if($magicQuote) $value = stripslashes($value);
if($value != stripslashes($old->$key))
{
$diff = '';
if(substr_count($value, "\n") > 1 or
substr_count($old->$key, "\n") > 1 or
strpos('name,title,desc,spec,steps,content,digest,verify,report', strtolower($key)) !== false)
{
$diff = commonModel::diff($old->$key, $value);
}
$changes[] = array('field' => $key, 'old' => $old->$key, 'new' => $value, 'diff' => $diff);
}
}
return $changes;
}
/**
* Get the full url of the system.
*
* @access public
* @return string
*/
public static function getSysURL()
{
$httpType = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http';
$httpHost = $_SERVER['HTTP_HOST'];
return "$httpType://$httpHost";
}
/**
* Print the run info.
*
* @param mixed $startTime the start time.
* @access public
* @return void
*/
public function printRunInfo($startTime)
{
vprintf($this->lang->runInfo, $this->common->getRunInfo($startTime));
}
}

View File

@@ -20,8 +20,6 @@ $lang->ellipsis = '...';
$lang->zentaoPMS = 'zentao';
$lang->welcome = "%s PMS";
$lang->myControl = "Dashboard";
$lang->currentPos = 'Current';
$lang->logout = 'Logout';
$lang->login = 'Login';
$lang->aboutZenTao = 'About';
@@ -31,25 +29,20 @@ $lang->runInfo = "<div class='row'><div class='u-1 a-center' id='debugbar
$lang->agreement = "I Agree to the <a href='http://zpl.pub/page/zplv12.html' target='_blank'>Z PUBLIC LICENSE 1.2</a>, <span class='text-danger'>and promise to keep the logo, link of ZenTao.</span>";
$lang->reset = 'Reset';
$lang->cancel = 'Cancel';
$lang->refresh = 'Refresh';
$lang->edit = 'Edit';
$lang->copy = 'Copy';
$lang->delete = 'Delete';
$lang->close = 'Close';
$lang->link = 'Link';
$lang->unlink = 'Unlink';
$lang->import = 'Import';
$lang->export = 'Export';
$lang->setFileName = 'Filename:';
$lang->activate = 'Activate';
$lang->submitting = 'Saving...';
$lang->save = 'Save';
$lang->confirm = 'Confirm';
$lang->preview = 'View';
$lang->goback = 'Back';
$lang->goPC = 'PC';
$lang->go = 'GO';
$lang->more = 'More';
$lang->day = 'Day';
@@ -59,7 +52,6 @@ $lang->history = 'History';
$lang->attatch = 'Attatch';
$lang->reverse = 'Reverse';
$lang->switchDisplay= 'Toggle Show';
$lang->switchHelp = 'Toggle Help';
$lang->addFiles = 'Add Files';
$lang->files = 'Files ';
$lang->pasteText = 'Paste text';
@@ -67,14 +59,19 @@ $lang->uploadImages = 'Upload images ';
$lang->timeout = 'Timed out, please check the network, or retry!';
$lang->repairTable = 'The table may be damaged, please repair by phpmyadmin or myisamchk!';
$lang->duplicate = '%s has the same title';
$lang->ipLimited = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /></head><body>Sorry, current IP is limited by Administrator. Please contact the Administrator to lift the restrictions.</body></html>";
$lang->unfold = '+';
$lang->fold = '-';
$lang->preShortcutKey = '[Shortcut key:←]';
$lang->nextShortcutKey = '[Shortcut key:→]';
$lang->select = 'Select';
$lang->selectAll = 'All';
$lang->selectReverse = 'Inverse';
$lang->loading = 'Wait...';
$lang->notFound = 'Sorry, the object not found.';
$lang->showAll = "[[Show all {$lang->projectCommon}s]]";
$lang->hideClosed = "[[Show {$lang->projectCommon}s going]]";
$lang->future = 'Future';
$lang->year = 'Year';
@@ -364,6 +361,7 @@ $lang->error->passwordrule = "Password should more than six letters.";
$lang->error->accessDenied = 'No purview';
$lang->error->pasteImg = 'Your browser does not support the paste image!';
$lang->error->noData = 'No data';
$lang->error->editedByOther = 'The record may have been changed. Please refresh and re edit!';
/* Pager. */
$lang->pager = new stdclass();
@@ -376,11 +374,7 @@ $lang->pager->next = "<i class='icon-play' title='Next'></i>";
$lang->pager->last = "<i class='icon-step-forward' title='Last'></i>";
$lang->pager->locate = "GO!";
$lang->zentaoSite = "Official Site";
$lang->chinaScrum = "<a href='http://api.zentao.net/goto.php?item=chinascrum' target='_blank'>Scrum community</a> ";
$lang->agileTraining = "<a href='http://api.zentao.net/goto.php?item=agiletrain' target='_blank'>Training</a> ";
$lang->donate = "<a href='http://api.zentao.net/goto.php?item=donate' target='_blank'>Donate</a> ";
$lang->proVersion = "<a href='http://api.zentao.net/goto.php?item=proversion&from=footer' target='_blank' class='text-important'><i class='text-danger icon-reply icon-rotate-90'></i> PRO</a> &nbsp; ";
$lang->proVersion = "<a href='http://api.zentao.net/goto.php?item=proversion&from=footer' target='_blank' class='text-important'>PRO <i class='text-danger icon-pro-version'></i></a> &nbsp; ";
$lang->downNotify = "Down notify";
$lang->suhosinInfo = "Warming:data is too large! Please enlarge the setting of <font color=red>sohusin.post.max_vars</font> and <font color=red>sohusin.request.max_vars</font> in php.ini. Otherwise partial data can't be saved.";
@@ -389,9 +383,8 @@ $lang->noticeImport = "<p style='font-size:14px'>Import data, containing the ex
$lang->noResultsMatch = "No results match";
$lang->searchMore = "Search more results with the key:";
$lang->selectAnOption = "Select an option";
$lang->selectSomeOptions = "Select some options";
$lang->chooseUsersToMail = "Choose users to mail...";
$lang->browserNotice = 'Using the browser may not be able to get the best browsing effect, it is recommended to use chrome, Firefox, IE9 +, opera, Safari browser.';
/* Date times. */
define('DT_DATETIME1', 'Y-m-d H:i:s');
@@ -486,6 +479,7 @@ $lang->icons['close'] = 'off';
$lang->icons['activate'] = 'off';
$lang->icons['review'] = 'search';
$lang->icons['confirm'] = 'search';
$lang->icons['confirmBug'] = 'search';
$lang->icons['putoff'] = 'calendar';
$lang->icons['suspend'] = 'pause';
$lang->icons['pause'] = 'pause';

View File

@@ -20,8 +20,6 @@ $lang->ellipsis = '…';
$lang->zentaoPMS = '禅道';
$lang->welcome = "%s项目管理系统";
$lang->myControl = "我的地盘";
$lang->currentPos = '当前位置:';
$lang->logout = '退出';
$lang->login = '登录';
$lang->aboutZenTao = '关于';
@@ -31,25 +29,20 @@ $lang->runInfo = "<div class='row'><div class='u-1 a-center' id='debugbar
$lang->agreement = "已阅读并同意<a href='http://zpl.pub/page/zplv12.html' target='_blank'>《Z PUBLIC LICENSE授权协议1.2》</a>。<span class='text-danger'>未经许可,不得去除、隐藏或遮掩禅道软件的任何标志及链接。</span>";
$lang->reset = '重填';
$lang->cancel = '取消';
$lang->refresh = '刷新';
$lang->edit = '编辑';
$lang->copy = '复制';
$lang->delete = '删除';
$lang->close = '关闭';
$lang->link = '关联';
$lang->unlink = '移除';
$lang->import = '导入';
$lang->export = '导出';
$lang->setFileName = '文件名:';
$lang->activate = '激活';
$lang->submitting = '稍候...';
$lang->save = '保存';
$lang->confirm = '确认';
$lang->preview = '查看';
$lang->goback = '返回';
$lang->goPC = 'PC版';
$lang->go = 'GO';
$lang->more = '更多';
$lang->day = '天';
@@ -59,7 +52,6 @@ $lang->history = '历史记录';
$lang->attatch = '附件';
$lang->reverse = '切换顺序';
$lang->switchDisplay= '切换显示';
$lang->switchHelp = '切换帮助';
$lang->addFiles = '上传了附件 ';
$lang->files = '附件 ';
$lang->pasteText = '粘贴文本 ';
@@ -67,14 +59,19 @@ $lang->uploadImages = '多图上传 ';
$lang->timeout = '连接超时,请检查网络环境,或重试!';
$lang->repairTable = '数据库表可能损坏请用phpmyadmin或myisamchk检查修复。';
$lang->duplicate = '已有相同标题的%s';
$lang->ipLimited = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /></head><body>抱歉管理员限制当前IP登录请联系管理员解除限制。</body></html>";
$lang->unfold = '+';
$lang->fold = '-';
$lang->preShortcutKey = '[快捷键:←]';
$lang->nextShortcutKey = '[快捷键:→]';
$lang->select = '选择';
$lang->selectAll = '全选';
$lang->selectReverse = '反选';
$lang->loading = '稍候...';
$lang->notFound = '抱歉,您访问的对象并不存在!';
$lang->showAll = '[[全部显示]]';
$lang->hideClosed = '[[显示进行中]]';
$lang->future = '未来';
$lang->year = '年';
@@ -364,6 +361,7 @@ $lang->error->passwordrule = "密码应该符合规则,长度至少为六
$lang->error->accessDenied = '您没有访问权限';
$lang->error->pasteImg = '您的浏览器不支持粘贴图片!';
$lang->error->noData = '没有数据';
$lang->error->editedByOther = '该记录可能已经被改动。请刷新页面重新编辑!';
/* 分页信息。*/
$lang->pager = new stdclass();
@@ -376,11 +374,7 @@ $lang->pager->next = "<i class='icon-play' title='下一页'></i>";
$lang->pager->last = "<i class='icon-step-forward' title='末页'></i>";
$lang->pager->locate = "GO!";
$lang->zentaoSite = "官方网站";
$lang->chinaScrum = "<a href='http://api.zentao.net/goto.php?item=chinascrum' target='_blank'>Scrum社区</a>&nbsp; ";
$lang->agileTraining = "<a href='http://api.zentao.net/goto.php?item=agiletrain' target='_blank'>培训</a> ";
$lang->donate = "<a href='http://api.zentao.net/goto.php?item=donate' target='_blank'>捐赠 </a>";
$lang->proVersion = "<a href='http://api.zentao.net/goto.php?item=proversion&from=footer' target='_blank' id='proLink' class='text-important'><i class='text-danger icon-reply icon-rotate-90'></i> 专业版!</a> &nbsp; ";
$lang->proVersion = "<a href='http://api.zentao.net/goto.php?item=proversion&from=footer' target='_blank' id='proLink' class='text-important'>专业版 <i class='text-danger icon-pro-version'></i></a> &nbsp; ";
$lang->downNotify = "下载桌面提醒";
$lang->suhosinInfo = "警告数据太多请在php.ini中修改<font color=red>sohusin.post.max_vars</font>和<font color=red>sohusin.request.max_vars</font>(设置更大的数)。 保存并重新启动apache否则会造成部分数据无法保存。";
@@ -389,9 +383,8 @@ $lang->noticeImport = "<p style='font-size:14px'>导入数据中,含有已经
$lang->noResultsMatch = "没有匹配结果";
$lang->searchMore = "搜索此关键字的更多结果:";
$lang->selectAnOption = "选择一个选项";
$lang->selectSomeOptions = "选择一些选项";
$lang->chooseUsersToMail = "选择要发信通知的用户...";
$lang->browserNotice = '你目前使用的浏览器可能无法得到最佳浏览效果建议使用Chrome、火狐、IE9+、Opera、Safari浏览器。';
/* 时间格式设置。*/
define('DT_DATETIME1', 'Y-m-d H:i:s');
@@ -486,6 +479,7 @@ $lang->icons['close'] = 'off';
$lang->icons['activate'] = 'off';
$lang->icons['review'] = 'search';
$lang->icons['confirm'] = 'search';
$lang->icons['confirmBug'] = 'search';
$lang->icons['putoff'] = 'calendar';
$lang->icons['suspend'] = 'pause';
$lang->icons['pause'] = 'pause';

View File

@@ -20,8 +20,6 @@ $lang->ellipsis = '…';
$lang->zentaoPMS = '禪道';
$lang->welcome = "%s項目管理系統";
$lang->myControl = "我的地盤";
$lang->currentPos = '當前位置:';
$lang->logout = '退出';
$lang->login = '登錄';
$lang->aboutZenTao = '關於';
@@ -31,25 +29,20 @@ $lang->runInfo = "<div class='row'><div class='u-1 a-center' id='debugbar
$lang->agreement = "已閲讀並同意<a href='http://zpl.pub/page/zplv12.html' target='_blank'>《Z PUBLIC LICENSE授權協議1.2》</a>。<span class='text-danger'>未經許可,不得去除、隱藏或遮掩禪道軟件的任何標誌及連結。</span>";
$lang->reset = '重填';
$lang->cancel = '取消';
$lang->refresh = '刷新';
$lang->edit = '編輯';
$lang->copy = '複製';
$lang->delete = '刪除';
$lang->close = '關閉';
$lang->link = '關聯';
$lang->unlink = '移除';
$lang->import = '導入';
$lang->export = '導出';
$lang->setFileName = '檔案名:';
$lang->activate = '激活';
$lang->submitting = '稍候...';
$lang->save = '保存';
$lang->confirm = '確認';
$lang->preview = '查看';
$lang->goback = '返回';
$lang->goPC = 'PC版';
$lang->go = 'GO';
$lang->more = '更多';
$lang->day = '天';
@@ -59,7 +52,6 @@ $lang->history = '歷史記錄';
$lang->attatch = '附件';
$lang->reverse = '切換順序';
$lang->switchDisplay= '切換顯示';
$lang->switchHelp = '切換幫助';
$lang->addFiles = '上傳了附件 ';
$lang->files = '附件 ';
$lang->pasteText = '粘貼文本 ';
@@ -67,14 +59,19 @@ $lang->uploadImages = '多圖上傳 ';
$lang->timeout = '連接超時,請檢查網絡環境,或重試!';
$lang->repairTable = '資料庫表可能損壞請用phpmyadmin或myisamchk檢查修復。';
$lang->duplicate = '已有相同標題的%s';
$lang->ipLimited = "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /></head><body>抱歉管理員限制當前IP登錄請聯繫管理員解除限制。</body></html>";
$lang->unfold = '+';
$lang->fold = '-';
$lang->preShortcutKey = '[快捷鍵:←]';
$lang->nextShortcutKey = '[快捷鍵:→]';
$lang->select = '選擇';
$lang->selectAll = '全選';
$lang->selectReverse = '反選';
$lang->loading = '稍候...';
$lang->notFound = '抱歉,您訪問的對象並不存在!';
$lang->showAll = '[[全部顯示]]';
$lang->hideClosed = '[[顯示進行中]]';
$lang->future = '未來';
$lang->year = '年';
@@ -364,6 +361,7 @@ $lang->error->passwordrule = "密碼應該符合規則,長度至少為六
$lang->error->accessDenied = '您沒有訪問權限';
$lang->error->pasteImg = '您的瀏覽器不支持粘貼圖片!';
$lang->error->noData = '沒有數據';
$lang->error->editedByOther = '該記錄可能已經被改動。請刷新頁面重新編輯!';
/* 分頁信息。*/
$lang->pager = new stdclass();
@@ -376,11 +374,7 @@ $lang->pager->next = "<i class='icon-play' title='下一頁'></i>";
$lang->pager->last = "<i class='icon-step-forward' title='末頁'></i>";
$lang->pager->locate = "GO!";
$lang->zentaoSite = "官方網站";
$lang->chinaScrum = "<a href='http://api.zentao.net/goto.php?item=chinascrum' target='_blank'>Scrum社區</a>&nbsp; ";
$lang->agileTraining = "<a href='http://api.zentao.net/goto.php?item=agiletrain' target='_blank'>培訓</a> ";
$lang->donate = "<a href='http://api.zentao.net/goto.php?item=donate' target='_blank'>捐贈 </a>";
$lang->proVersion = "<a href='http://api.zentao.net/goto.php?item=proversion&from=footer' target='_blank' id='proLink' class='text-important'><i class='text-danger icon-reply icon-rotate-90'></i> 專業版!</a> &nbsp; ";
$lang->proVersion = "<a href='http://api.zentao.net/goto.php?item=proversion&from=footer' target='_blank' id='proLink' class='text-important'>專業版 <i class='text-danger icon-pro-version'></i></a> &nbsp; ";
$lang->downNotify = "下載桌面提醒";
$lang->suhosinInfo = "警告數據太多請在php.ini中修改<font color=red>sohusin.post.max_vars</font>和<font color=red>sohusin.request.max_vars</font>(設置更大的數)。 保存並重新啟動apache否則會造成部分數據無法保存。";
@@ -389,9 +383,8 @@ $lang->noticeImport = "<p style='font-size:14px'>導入數據中,含有已經
$lang->noResultsMatch = "沒有匹配結果";
$lang->searchMore = "搜索此關鍵字的更多結果:";
$lang->selectAnOption = "選擇一個選項";
$lang->selectSomeOptions = "選擇一些選項";
$lang->chooseUsersToMail = "選擇要發信通知的用戶...";
$lang->browserNotice = '你目前使用的瀏覽器可能無法得到最佳瀏覽效果建議使用Chrome、火狐、IE9+、Opera、Safari瀏覽器。';
/* 時間格式設置。*/
define('DT_DATETIME1', 'Y-m-d H:i:s');
@@ -486,6 +479,7 @@ $lang->icons['close'] = 'off';
$lang->icons['activate'] = 'off';
$lang->icons['review'] = 'search';
$lang->icons['confirm'] = 'search';
$lang->icons['confirmBug'] = 'search';
$lang->icons['putoff'] = 'calendar';
$lang->icons['suspend'] = 'pause';
$lang->icons['pause'] = 'pause';

View File

@@ -11,6 +11,30 @@
*/
class commonModel extends model
{
/**
* The construc method, to do some auto things.
*
* @access public
* @return void
*/
public function __construct()
{
parent::__construct();
if(!defined('FIRST_RUN'))
{
$this->startSession();
$this->sendHeader();
$this->setCompany();
$this->setUser();
$this->loadConfigFromDB();
$this->loadCustomFromDB();
if(!$this->checkIP()) die($this->lang->ipLimited);
if($this->app->getViewType() == 'mhtml') $this->setMobileMenu();
$this->app->loadLang('company');
define('FIRST_RUN', true);
}
}
/**
* Start the session.
*
@@ -102,21 +126,8 @@ class commonModel extends model
$this->config->personal = isset($config[$account]) ? $config[$account] : array();
/* Overide the items defined in config/config.php and config/my.php. */
if(isset($this->config->system->common))
{
foreach($this->config->system->common as $record)
{
if($record->section)
{
if(!isset($this->config->{$record->section})) $this->config->{$record->section} = new stdclass();
$this->config->{$record->section}->{$record->key} = $record->value;
}
else
{
if(!$record->section) $this->config->{$record->key} = $record->value;
}
}
}
if(isset($this->config->system->common)) helper::mergeConfig($this->config->system->common, 'common');
if(isset($this->config->personal->common)) helper::mergeConfig($this->config->personal->common, 'common');
}
/**
@@ -145,9 +156,10 @@ class commonModel extends model
*/
public function isOpenMethod($module, $method)
{
if($module == 'user' and strpos('login|logout|deny', $method) !== false) return true;
if($module == 'user' and strpos('login|logout|deny|reset', $method) !== false) return true;
if($module == 'api' and $method == 'getsessionid') return true;
if($module == 'misc' and $method == 'ping') return true;
if($module == 'misc' and $method == 'checktable') return true;
if($module == 'block' and $method == 'main') return true;
if($module == 'sso' and $method == 'login') return true;
if($module == 'sso' and $method == 'logout') return true;
@@ -161,7 +173,6 @@ class commonModel extends model
if(stripos($method, 'ajaxgetdropmenu') !== false) return true;
if(stripos($method, 'ajaxgetmatcheditems') !== false) return true;
if($method == 'ajaxgetdetail' and $this->app->viewType == 'mhtml') return true;
if($module == 'misc' and $method == 'qrcode') return true;
if($module == 'misc' and $method == 'about') return true;
if($module == 'misc' and $method == 'checkupdate') return true;
@@ -176,6 +187,14 @@ class commonModel extends model
*/
public function deny($module, $method)
{
/* Get authorize again. */
$user = $this->app->user;
$user->rights = $this->loadModel('user')->authorize($user->account);
$user->groups = $this->user->getGroups($user->account);
$this->session->set('user', $user);
$this->app->user = $this->session->user;
if(commonModel::hasPriv($module, $method)) return true;
$vars = "module=$module&method=$method";
if(isset($this->server->http_referer))
{
@@ -198,17 +217,18 @@ class commonModel extends model
}
/**
* Get the run info.
* Print the run info.
*
* @param mixed $startTime the start time of this execution
* @param mixed $startTime the start time.
* @access public
* @return array the run info array.
*/
public function getRunInfo($startTime)
public function printRunInfo($startTime)
{
$info['timeUsed'] = round(getTime() - $startTime, 4) * 1000;
$info['memory'] = round(memory_get_peak_usage() / 1024, 1);
$info['querys'] = count(dao::$querys);
vprintf($this->lang->runInfo, $info);
return $info;
}
@@ -241,7 +261,7 @@ class commonModel extends model
}
$isLeft = ($app->company->website and $app->company->backyard) ? '' : ' left';
echo "<li class='dropdown-submenu{$isLeft}'>";
echo "<a href='javascript:;'>" . $lang->theme . "</a><ul class='dropdown-menu'>";
foreach ($app->lang->themes as $key => $value)
@@ -285,7 +305,7 @@ class commonModel extends model
public function setMobileMenu()
{
$menu = new stdclass();
$role = isset($this->app->user->role) ? $this->app->user->role : '';
$this->config->locate = new stdclass();
@@ -299,31 +319,19 @@ class commonModel extends model
$bug = $this->lang->my->menu->bug['link'];
$project = $this->lang->menu->project . '|locate=no&&status=isdoing';
$product = $this->lang->menu->product . '|locate=no';
$menu = array('todo' => $todo, 'task' => $task, 'bug' => $bug, 'project' => $project, 'product' => $product);
if($role == 'dev' or $role == 'td' or $role == 'pm')
{
$menu = array('todo' => $todo, 'task' => $task, 'bug' => $bug, 'product' => $product, 'project' => $project);
}
elseif($role == 'pd' or $role == 'po')
{
$menu = array('todo' => $todo, 'story' => $story, 'bug' => $bug, 'product' => $product, 'project' => $project);
}
elseif($role == 'qa' or $role == 'qd')
{
$menu = array('todo' => $todo, 'bug' => $bug, 'project' => $project, 'product' => $product);
}
elseif($role == 'top')
{
$menu = array('project' => $project, 'product' => $product, 'todo' => $todo);
if($role and strpos('dev,td,pm', $role) !== false) $menu = array('todo' => $todo, 'task' => $task, 'bug' => $bug, 'product' => $product, 'project' => $project);
if($role and strpos('pd,po', $role) !== false) $menu = array('todo' => $todo, 'story' => $story, 'bug' => $bug, 'product' => $product, 'project' => $project);
if($role and strpos('qa,qd', $role) !== false) $menu = array('todo' => $todo, 'bug' => $bug, 'project' => $project, 'product' => $product);
if($role and strpos('top', $role) !== false) $menu = array('project' => $project, 'product' => $product, 'todo' => $todo);
if($role == 'top')
{
$this->config->locate->module = 'project';
$this->config->locate->method = 'index';
$this->config->locate->params = 'locate=no&status=doing';
}
else
{
$menu = array('todo' => $todo, 'task' => $task, 'bug' => $bug, 'project' => $project, 'product' => $product);
}
unset($this->lang->menuOrder);
unset($this->lang->menugroup);
@@ -343,7 +351,7 @@ class commonModel extends model
{
global $app, $lang;
echo "<ul class='nav'>\n";
/* Set the main main menu. */
$mainMenu = $moduleName;
if(isset($lang->menugroup->$moduleName)) $mainMenu = $lang->menugroup->$moduleName;
@@ -384,7 +392,7 @@ class commonModel extends model
list($menuLabel, $module, $method) = $link;
$vars = isset($link[3]) ? $link[3] : '';
if(common::hasPriv($module, $method))
if(commonModel::hasPriv($module, $method))
{
$link = helper::createLink($module, $method, $vars);
echo "<li $active><a href='$link' $active id='menu$menuKey'>$menuLabel</a></li>\n";
@@ -506,7 +514,7 @@ class commonModel extends model
$link = explode('|', $link);
list($label, $module, $method) = $link;
$vars = isset($link[3]) ? $link[3] : '';
if(common::hasPriv($module, $method))
if(commonModel::hasPriv($module, $method))
{
/* Is the currentModule active? */
$subModules = explode(',', $subModule);
@@ -564,10 +572,7 @@ class commonModel extends model
{
if(strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'windows') !== false)
{
global $app, $lang;
$notifyFile = $app->getBasePath() . 'www/data/notify/notify.zip';
if(!file_exists($notifyFile)) return false;
global $lang;
echo html::a(helper::createLink('misc', 'downNotify'), "<i class='icon-bell'></i>", '', "title='$lang->downNotify'") . ' &nbsp; ';
}
}
@@ -588,6 +593,275 @@ class commonModel extends model
echo '<script>$(function(){$("#qrcodeBtn").hover(function(){$(".popover-content img").attr("src", "' . helper::createLink('misc', 'qrCode') . '");});});</script>';
}
/**
* Print the link contains orderBy field.
*
* This method will auto set the orderby param according the params. Fox example, if the order by is desc,
* will be changed to asc.
*
* @param string $fieldName the field name to sort by
* @param string $orderBy the order by string
* @param string $vars the vars to be passed
* @param string $label the label of the link
* @param string $module the module name
* @param string $method the method name
* @static
* @access public
* @return void
*/
public static function printOrderLink($fieldName, $orderBy, $vars, $label, $module = '', $method = '')
{
global $lang, $app;
if(empty($module)) $module = $app->getModuleName();
if(empty($method)) $method = $app->getMethodName();
$className = 'header';
$order = explode('_', $orderBy);
$order[0] = trim($order[0], '`');
if($order[0] == $fieldName)
{
if(isset($order[1]) and $order[1] == 'asc')
{
$orderBy = "{$order[0]}_desc";
$className = 'headerSortDown';
}
else
{
$orderBy = "{$order[0]}_asc";
$className = 'headerSortUp';
}
}
else
{
$orderBy = "" . trim($fieldName, '`') . "" . '_' . 'asc';
$className = 'header';
}
$link = helper::createLink($module, $method, sprintf($vars, $orderBy));
echo "<div class='$className'>" . html::a($link, $label) . '</div>';
}
/**
* Print link to an modules' methd.
*
* Before printing, check the privilege first. If no privilege, return fasle. Else, print the link, return true.
*
* @param string $module the module name
* @param string $method the method
* @param string $vars vars to be passed
* @param string $label the label of the link
* @param string $target the target of the link
* @param string $misc others
* @param bool $newline
* @static
* @access public
* @return bool
*/
public static function printLink($module, $method, $vars = '', $label, $target = '', $misc = '', $newline = true, $onlyBody = false)
{
if(!commonModel::hasPriv($module, $method)) return false;
echo html::a(helper::createLink($module, $method, $vars, '', $onlyBody), $label, $target, $misc, $newline);
return true;
}
/**
* Print icon of split line.
*
* @static
* @access public
* @return void
*/
public static function printDivider()
{
echo "&nbsp;&nbsp;&nbsp;&nbsp;";
}
/**
* Print icon of comment.
*
* @param string $module
* @static
* @access public
* @return void
*/
public static function printCommentIcon($module)
{
if(isonlybody()) return false;
global $lang;
if(!commonModel::hasPriv($module, 'edit')) return false;
echo html::a('#commentBox', '<i class="icon-comment-alt"></i>', '', "title='$lang->comment' onclick='setComment()' class='btn'");
}
/**
* Print link icon.
*
* @param string $module
* @param string $method
* @param string $vars
* @param object $object
* @param string $type button|list
* @param string $icon
* @param string $target
* @param string $extraClass
* @param bool $onlyBody
* @param string $misc
* @static
* @access public
* @return void
*/
public static function printIcon($module, $method, $vars = '', $object = '', $type = 'button', $icon = '', $target = '', $extraClass = '', $onlyBody = false, $misc = '')
{
if(isonlybody() and strpos($extraClass, 'showinonlybody') === false) return false;
global $app, $lang;
/* Judge the $method of $module clickable or not, default is clickable. */
$clickable = true;
if(is_object($object))
{
if($app->getModuleName() != $module) $app->control->loadModel($module);
$modelClass = class_exists("ext{$module}Model") ? "ext{$module}Model" : $module . "Model";
if(class_exists($modelClass) and is_callable(array($modelClass, 'isClickable')))
{
$clickable = call_user_func_array(array($modelClass, 'isClickable'), array('object' => $object, 'method' => $method));
}
}
/* Set module and method, then create link to it. */
if(strtolower($module) == 'story' and strtolower($method) == 'createcase') ($module = 'testcase') and ($method = 'create');
if(strtolower($module) == 'bug' and strtolower($method) == 'tostory') ($module = 'story') and ($method = 'create');
if(strtolower($module) == 'bug' and strtolower($method) == 'createcase') ($module = 'testcase') and ($method = 'create');
if(!commonModel::hasPriv($module, $method)) return false;
$link = helper::createLink($module, $method, $vars, '', $onlyBody);
/* Set the icon title, try search the $method defination in $module's lang or $common's lang. */
$title = $method;
if($method == 'create' and $icon == 'copy') $method = 'copy';
if(isset($lang->$method) and is_string($lang->$method)) $title = $lang->$method;
if((isset($lang->$module->$method) or $app->loadLang($module)) and isset($lang->$module->$method))
{
$title = $method == 'report' ? $lang->$module->$method->common : $lang->$module->$method;
}
if($icon == 'toStory') $title = $lang->bug->toStory;
if($icon == 'createBug') $title = $lang->testtask->createBug;
/* set the class. */
if(!$icon)
{
$icon = $lang->icons[$method] ? $lang->icons[$method] : $method;
}
if(strpos(',edit,copy,report,export,delete,', ",$method,") !== false) $module = 'common';
$class = "icon-$module-$method";
if(!$clickable) $class .= ' disabled';
if($icon) $class .= ' icon-' . $icon;
/* Create the icon link. */
if($clickable)
{
if($app->getViewType() == 'mhtml')
{
echo html::a($link, $title, $target, "class='$extraClass' data-role='button' data-mini='true' data-inline='true' data-theme='b'", true);
return;
}
if($type == 'button')
{
if($method != 'edit' and $method != 'copy' and $method != 'delete')
{
echo html::a($link, "<i class='$class'></i> " . $title, $target, "class='btn $extraClass' $misc", true);
}
else
{
echo html::a($link, "<i class='$class'></i>", $target, "class='btn $extraClass' title='$title' $misc", false);
}
}
else
{
echo html::a($link, "<i class='$class'></i>", $target, "class='btn-icon $extraClass' title='$title' $misc", false);
}
}
else
{
if($type == 'list')
{
echo "<button type='button' class='disabled btn-icon $extraClass'><i class='$class' title='$title' $misc></i></button>";
}
}
}
/**
* Print backLink and preLink and nextLink.
*
* @param string $backLink
* @param object $preAndNext
* @access public
* @return void
*/
static public function printRPN($backLink, $preAndNext = '', $linkTemplate = '')
{
global $lang, $app;
if(isonlybody()) return false;
echo html::a($backLink, '<i class="icon-goback icon-level-up icon-large icon-rotate-270"></i>', '', "class='btn' title={$lang->goback}");
if(isset($preAndNext->pre) and $preAndNext->pre)
{
$id = (isset($_SESSION['testcaseOnlyCondition']) and !$_SESSION['testcaseOnlyCondition'] and $app->getModuleName() == 'testcase' and isset($preAndNext->pre->case)) ? 'case' : 'id';
$title = isset($preAndNext->pre->title) ? $preAndNext->pre->title : $preAndNext->pre->name;
$title = '#' . $preAndNext->pre->$id . ' ' . $title . ' ' . $lang->preShortcutKey;
$link = $linkTemplate ? sprintf($linkTemplate, $preAndNext->pre->$id) : inLink('view', "ID={$preAndNext->pre->$id}");
echo html::a($link, '<i class="icon-pre icon-chevron-left"></i>', '', "id='pre' class='btn' title='{$title}'");
}
if(isset($preAndNext->next) and $preAndNext->next)
{
$id = (isset($_SESSION['testcaseOnlyCondition']) and !$_SESSION['testcaseOnlyCondition'] and $app->getModuleName() == 'testcase' and isset($preAndNext->next->case)) ? 'case' : 'id';
$title = isset($preAndNext->next->title) ? $preAndNext->next->title : $preAndNext->next->name;
$title = '#' . $preAndNext->next->$id . ' ' . $title . ' ' . $lang->nextShortcutKey;
$link = $linkTemplate ? sprintf($linkTemplate, $preAndNext->next->$id) : inLink('view', "ID={$preAndNext->next->$id}");
echo html::a($link, '<i class="icon-pre icon-chevron-right"></i>', '', "id='next' class='btn' title='$title'");
}
}
/**
* Create changes of one object.
*
* @param mixed $old the old object
* @param mixed $new the new object
* @static
* @access public
* @return array
*/
public static function createChanges($old, $new)
{
global $config;
$changes = array();
$magicQuote = get_magic_quotes_gpc();
foreach($new as $key => $value)
{
if(strtolower($key) == 'lastediteddate') continue;
if(strtolower($key) == 'lasteditedby') continue;
if(strtolower($key) == 'assigneddate') continue;
if(strtolower($key) == 'editedby') continue;
if(strtolower($key) == 'editeddate') continue;
if($magicQuote) $value = stripslashes($value);
if($value != stripslashes($old->$key))
{
$diff = '';
if(substr_count($value, "\n") > 1 or
substr_count($old->$key, "\n") > 1 or
strpos('name,title,desc,spec,steps,content,digest,verify,report', strtolower($key)) !== false)
{
$diff = commonModel::diff($old->$key, $value);
}
$changes[] = array('field' => $key, 'old' => $old->$key, 'new' => $value, 'diff' => $diff);
}
}
return $changes;
}
/**
* Diff two string. (see phpt)
*
@@ -819,4 +1093,186 @@ class commonModel extends model
}
return $hasField;
}
/**
* Check upgrade's status file is ok or not.
*
* @access public
* @return void
*/
public function checkUpgradeStatus()
{
$statusFile = $this->loadModel('upgrade')->checkSafeFile();
if($statusFile)
{
echo "<html><head><meta http-equiv='Content-Type' content='text/html; charset=utf-8' /></head><body>";
echo "<table align='center' style='margin-top:100px; border:1px solid gray; font-size:14px;'><tr><td>";
printf($this->lang->upgrade->setStatusFile, $statusFile, $statusFile, $statusFile);
die('</td></tr></table></body></html>');
}
}
/**
* Check the user has permission to access this method, if not, locate to the login page or deny page.
*
* @access public
* @return void
*/
public function checkPriv()
{
$module = $this->app->getModuleName();
$method = $this->app->getMethodName();
if($this->isOpenMethod($module, $method)) return true;
if(!$this->loadModel('user')->isLogon() and $this->server->php_auth_user) $this->user->identifyByPhpAuth();
if(!$this->loadModel('user')->isLogon() and $this->cookie->za) $this->user->identifyByCookie();
if(isset($this->app->user))
{
if(!commonModel::hasPriv($module, $method)) $this->deny($module, $method);
}
else
{
$referer = helper::safe64Encode($this->app->getURI(true));
die(js::locate(helper::createLink('user', 'login', "referer=$referer")));
}
}
/**
* Check the user has permisson of one method of one module.
*
* @param string $module
* @param string $method
* @static
* @access public
* @return bool
*/
public static function hasPriv($module, $method)
{
global $app, $lang;
/* Check is the super admin or not. */
$account = ',' . $app->user->account . ',';
if(strpos($app->company->admins, $account) !== false) return true;
/* If not super admin, check the rights. */
$rights = $app->user->rights['rights'];
$acls = $app->user->rights['acls'];
$module = strtolower($module);
$method = strtolower($method);
if(isset($rights[$module][$method]))
{
if(empty($acls['views'])) return true;
$menu = isset($lang->menugroup->$module) ? $lang->menugroup->$module : $module;
$menu = strtolower($menu);
if($menu != 'qa' and !isset($lang->$menu->menu)) return true;
if($menu == 'my' or $menu == 'index' or $module == 'tree') return true;
if($module == 'company' and $method == 'dynamic') return true;
if($module == 'action' and $method == 'editcomment') return true;
if(!isset($acls['views'][$menu])) return false;
return true;
}
return false;
}
/**
* Check whether IP in white list.
*
* @access public
* @return bool
*/
public function checkIP()
{
$ip = $this->server->remote_addr;
$ipWhiteList = $this->config->ipWhiteList;
/* If the ip white list is '*'. */
if($ipWhiteList == '*') return true;
/* The ip is same as ip in white list. */
if($ip == $ipWhiteList) return true;
/* If the ip in white list is like 192.168.1.1-192.168.1.10. */
if(strpos($ipWhiteList, '-') !== false)
{
list($min, $max) = explode('-', $ipWhiteList);
$min = ip2long(trim($min));
$max = ip2long(trim($max));
$ip = ip2long(trim($ip));
return $ip >= $min and $ip <= $max;
}
/* If the ip in white list is in IP/CIDR format eg 127.0.0.1/24. Thanks to zcat. */
if(strpos($ipWhiteList, '/') == false) $ipWhiteList .= '/32';
list($ipWhiteList, $netmask) = explode('/', $ipWhiteList, 2);
$ip = ip2long($ip);
$ipWhiteList = ip2long($ipWhiteList);
$wildcard = pow(2, (32 - $netmask)) - 1;
$netmask = ~ $wildcard;
return (($ip & $netmask) == ($ipWhiteList & $netmask));
}
/**
* Replace the %s of one key of a menu by $params.
*
* All the menus are defined in the common's language file. But there're many dynamic params, so in the defination,
* we used %s as placeholder. These %s should be setted in one module.
*
* The items of one module's menu may be an string or array. For example, please see module/common/lang.
*
* @param string $object the menus of one module
* @param string $key the menu item to be replaced
* @param string $params the params passed to the menu item
* @access public
* @return void
*/
public static function setMenuVars($menu, $key, $params)
{
if(is_array($params))
{
if(is_array($menu->$key))
{
$menu->$key = (object)$menu->$key;
$menu->$key->link = vsprintf($menu->$key->link, $params);
$menu->$key = (array)$menu->$key;
}
else
{
$menu->$key = vsprintf($menu->$key, $params);
}
}
else
{
if(is_array($menu->$key))
{
$menu->$key = (object)$menu->$key;
$menu->$key->link = sprintf($menu->$key->link, $params);
$menu->$key = (array)$menu->$key;
}
else
{
$menu->$key = sprintf($menu->$key, $params);
}
}
}
/**
* Get the full url of the system.
*
* @access public
* @return string
*/
public static function getSysURL()
{
$httpType = (isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == 'on') ? 'https' : 'http';
$httpHost = $_SERVER['HTTP_HOST'];
return "$httpType://$httpHost";
}
}
class common extends commonModel
{
}

View File

@@ -2,6 +2,7 @@
#actionbox a{font-weight:normal}
.col-side fieldset#actionbox{padding-right:5px;}
.col-side #actionbox #historyItem li span.item{white-space:nowrap}
.changes blockquote{font-family: monospace, serif;}
</style>
<script language='Javascript'>
var fold = '<?php echo $lang->fold;?>';
@@ -29,15 +30,15 @@ function switchChange(historyID)
function toggleStripTags(obj)
{
var btn = $(obj);
var diffTag = btn.find('.diff-all');
var diffTag = btn.find('.icon-file-code');
if(diffTag.length)
{
diffTag.removeClass('diff-all').addClass('diff-short');
diffTag.removeClass('icon-file-code').addClass('diff-short');
btn.attr('title', '<?php echo $lang->action->textDiff?>');
}
else
{
btn.find('.diff-short').removeClass('diff-short').addClass('diff-all');
btn.find('.diff-short').removeClass('diff-short').addClass('icon-file-code');
btn.attr('title', '<?php echo $lang->action->original?>');
}
var boxObj = $(obj).next();
@@ -89,7 +90,7 @@ function toggleComment(actionID)
$(function()
{
var diffButton = "<a href='javascript:;' onclick='toggleStripTags(this)' class='changeDiff btn-icon' style='display:none;' title='<?php echo $lang->action->original?>'><i class='icon- diff-all'></i></a>";
var diffButton = "<a href='javascript:;' onclick='toggleStripTags(this)' class='changeDiff btn-icon' style='display:none;' title='<?php echo $lang->action->original?>'><i class='icon- icon-file-code'></i></a>";
var newBoxID = ''
var oldBoxID = ''
$('blockquote').each(function()
@@ -146,7 +147,7 @@ $(function()
<?php echo $this->action->printChanges($action->objectType, $action->history);?>
</div>
<?php if($canEditComment):?>
<span class='pull-right comment<?php echo $action->id;?>'><?php echo html::a('javascript:toggleComment(' . $action->id . ')', '<i class="icon-pencil"></i>', '', "class='btn btn-mini'")?></span>
<span class='pull-right comment<?php echo $action->id;?>'><?php echo html::a('javascript:toggleComment(' . $action->id . ')', '<i class="icon-pencil"></i>', '', "class='btn btn-mini' style='border:none'")?></span>
<?php endif;?>
<?php
if($action->comment)

View File

@@ -19,6 +19,15 @@
</div>
</div>
<?php endif;?>
<?php if(!isset($config->global->browserNotice)):?>
<script>
browserNotice = '<?php echo $lang->browserNotice?>'
function ajaxIgnoreBrowser(){$.get(createLink('misc', 'ajaxIgnoreBrowser'));}
$(function(){showBrowserNotice()});
</script>
<?php endif;?>
<?php
js::set('onlybody', $onlybody); // set the onlybody var.
if($this->loadModel('cron')->runable()) js::execute('startCron()');

View File

@@ -19,23 +19,23 @@ $clientTheme = $this->app->getClientTheme();
js::exportConfigVars();
if($config->debug)
{
js::import($jsRoot . 'jquery/lib.js', $config->version);
js::import($jsRoot . 'zui/min.js', $config->version);
js::import($jsRoot . 'my.min.js', $config->version);
js::import($jsRoot . 'jquery/lib.js');
js::import($jsRoot . 'zui/min.js');
js::import($jsRoot . 'my.min.js');
css::import($themeRoot . 'zui/css/min.css', $config->version);
css::import($defaultTheme . 'style.css', $config->version);
css::import($themeRoot . 'zui/css/min.css');
css::import($defaultTheme . 'style.css');
css::import($langTheme, $config->version);
if(strpos($clientTheme, 'default') === false) css::import($clientTheme . 'style.css', $config->version);
css::import($langTheme);
if(strpos($clientTheme, 'default') === false) css::import($clientTheme . 'style.css');
}
else
{
js::import($jsRoot . 'all.js', $config->version);
css::import($defaultTheme . $this->cookie->lang . '.' . $this->cookie->theme . '.css', $config->version);
js::import($jsRoot . 'all.js');
css::import($defaultTheme . $this->cookie->lang . '.' . $this->cookie->theme . '.css');
}
if(isset($pageCss)) css::internal($pageCss);
if(isset($pageCSS)) css::internal($pageCSS);
echo html::favicon($webRoot . 'favicon.ico');
?>

View File

@@ -19,22 +19,22 @@ $clientTheme = $this->app->getClientTheme();
js::exportConfigVars();
if($config->debug)
{
js::import($jsRoot . 'jquery/mobile/jquery-1.10.1.min.js', $config->version);
js::import($jsRoot . 'm.my.full.js', $config->version);
js::import($jsRoot . 'jquery/mobile/jquery.mobile.min.js', $config->version);
js::import($jsRoot . 'jquery/jquery.pjax.js', $config->version);
js::import($jsRoot . 'jquery/mobile/jquery-1.10.1.min.js');
js::import($jsRoot . 'm.my.full.js');
js::import($jsRoot . 'jquery/mobile/jquery.mobile.min.js');
js::import($jsRoot . 'jquery/jquery.pjax.js');
css::import($defaultTheme . 'jquery.mobile.css', $config->version);
css::import($defaultTheme . 'm.style.css', $config->version);
css::import($langTheme, $config->version);
css::import($defaultTheme . 'jquery.mobile.css');
css::import($defaultTheme . 'm.style.css');
css::import($langTheme);
}
else
{
js::import($jsRoot . 'm.all.js', $config->version);
css::import($defaultTheme . 'm.' . $this->cookie->lang . '.default.css', $config->version);
js::import($jsRoot . 'm.all.js');
css::import($defaultTheme . 'm.' . $this->cookie->lang . '.default.css');
}
if(isset($pageCss)) css::internal($pageCss);
if(isset($pageCSS)) css::internal($pageCSS);
echo html::favicon($webRoot . 'favicon.ico');
?>

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