| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951 |
- <?php
- /**
- * The model file of kanban module of ZenTaoPMS.
- *
- * @copyright Copyright 2009-2023 禅道软件(青岛)有限公司(ZenTao Software (Qingdao) Co., Ltd. www.cnezsoft.com)
- * @license ZPL(http://zpl.pub/page/zplv12.html) or AGPL(https://www.gnu.org/licenses/agpl-3.0.en.html)
- * @author Shujie Tian <tianshujie@easycorp.ltd>
- * @package kanban
- * @version $Id: model.php 5118 2021-10-22 10:18:41Z $
- * @link https://www.zentao.net
- */
- class kanbanModel extends model
- {
- /**
- * 创建看板分组。
- * Create a kanban group.
- *
- * @param int $kanbanID
- * @param int $regionID
- * @access public
- * @return int|false
- */
- public function createGroup($kanbanID, $regionID)
- {
- $maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBANGROUP)
- ->where('region')->eq($regionID)
- ->fetch('maxOrder');
- $order = $maxOrder ? $maxOrder + 1 : 1;
- $group = new stdclass();
- $group->kanban = $kanbanID;
- $group->region = $regionID;
- $group->order = $order;
- $this->dao->insert(TABLE_KANBANGROUP)->data($group)->autoCheck()->exec();
- if(dao::isError()) return false;
- return $this->dao->lastInsertID();
- }
- /**
- * 创建默认看板区域。
- * Create a default kanban region.
- *
- * @param object $kanban
- * @access public
- * @return int|bool
- */
- public function createDefaultRegion($kanban)
- {
- $region = new stdclass();
- $region->name = $this->lang->kanbanregion->default;
- $region->kanban = $kanban->id;
- $region->space = $kanban->space;
- $region->createdBy = $this->app->user->account;
- $region->createdDate = helper::now();
- return $this->createRegion($kanban, $region);
- }
- /**
- * 复制看板区域。
- * Copy kanban regions.
- *
- * @param object $kanban
- * @param int $copyKanbanID
- * @param string $from kanban|execution
- * @param string $param
- * @access public
- * @return void
- */
- public function copyRegions($kanban, $copyKanbanID, $from = 'kanban', $param = 'withArchived')
- {
- if(empty($kanban) || empty($copyKanbanID)) return;
- $regions = $this->getRegionPairs($copyKanbanID, 0, $from);
- $order = 1;
- foreach($regions as $copyID => $copyName)
- {
- $region = new stdclass();
- $region->name = $copyName;
- $region->kanban = $kanban->id;
- $region->space = $kanban->space;
- $region->createdBy = $this->app->user->account;
- $region->createdDate = helper::now();
- $region->order = $order;
- $this->createRegion($kanban, $region, $copyID, $from, $param);
- $order ++;
- }
- }
- /**
- * 创建区域。
- * Create a new region.
- *
- * @param object $kanban
- * @param object $fromRegion
- * @param int $copyRegionID
- * @param string $from kanban|execution
- * @param string $param
- * @access public
- * @return int
- */
- public function createRegion($kanban, $fromRegion = null, $copyRegionID = 0, $from = 'kanban', $param = '')
- {
- $account = $this->app->user->account;
- $order = 1;
- $region = $fromRegion;
- if(empty($region))
- {
- $maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBANREGION)
- ->where('kanban')->eq($kanban->id)
- ->fetch('maxOrder');
- $order = $maxOrder ? $maxOrder + 1 : 1;
- $region = new stdclass();
- $region->kanban = $kanban->id;
- $region->space = $from == 'kanban' ? $kanban->space : 0;
- $region->name = $this->post->name;
- $region->createdBy = $account;
- $region->createdDate = helper::now();
- }
- $region->order = isset($region->order) ? $region->order : $order;
- $this->dao->insert(TABLE_KANBANREGION)->data($region)
- ->batchCheck($this->config->kanban->require->createregion, 'notempty')
- ->check('name', 'unique', "kanban = {$kanban->id} AND deleted = '0' AND space='$region->space'")
- ->autoCheck()
- ->exec();
- $regionID = $this->dao->lastInsertID();
- if(dao::isError()) return false;
- $this->loadModel('action')->create('kanbanRegion', $regionID, 'Created');
- if($copyRegionID)
- {
- $this->copyRegion($kanban, $regionID, $copyRegionID, $from, $param);
- }
- elseif($from == 'kanban')
- {
- $groupID = $this->createGroup((int)$kanban->id, $regionID);
- if(dao::isError()) return false;
- $this->createDefaultLane($regionID, $groupID);
- if(dao::isError()) return false;
- $this->createDefaultColumns($regionID, $groupID);
- if(dao::isError()) return false;
- }
- return $regionID;
- }
- /**
- * 复制区域。
- * Copy a region.
- *
- * @param object $kanban
- * @param int $regionID
- * @param int $copyRegionID
- * @param string $from
- * @param string $param
- * @access public
- * @return void
- */
- public function copyRegion($kanban, $regionID, $copyRegionID, $from = 'kanban', $param = '')
- {
- /* Gets the groups, lanes and columns of the replication region. */
- $copyGroups = $this->getGroupGroupByRegions((array)$copyRegionID);
- $copyLaneGroup = $this->getLaneGroupByRegions((array)$copyRegionID);
- $copyColumnGroup = $this->getColumnGroupByRegions((array)$copyRegionID, 'id_asc', $param);
- /* Create groups, lanes, and columns. */
- if(empty($copyGroups)) return $regionID;
- foreach($copyGroups[$copyRegionID] as $copyGroupID => $copyGroup)
- {
- $newGroupID = $this->createGroup((int)$kanban->id, $regionID);
- if(dao::isError()) return false;
- $copyLanes = isset($copyLaneGroup[$copyGroupID]) ? $copyLaneGroup[$copyGroupID] : array();
- $copyColumns = isset($copyColumnGroup[$copyGroupID]) ? $copyColumnGroup[$copyGroupID] : array();
- $lanePairs = $this->copyLanes($kanban, $copyLanes, $regionID, $newGroupID);
- $this->copyColumns($copyColumns, $regionID, $newGroupID, $from);
- if(dao::isError()) return false;
- if($param == 'updateTaskCell')
- {
- foreach($lanePairs as $oldLaneID => $newLaneID)
- {
- $cards = $this->dao->select('id,cards')->from(TABLE_KANBANCELL)->where('lane')->eq($oldLaneID)->andWhere('type')->eq('task')->fetchPairs();
- $cards = implode(',', $cards);
- $cards = preg_replace('/[,]+/', ',',$cards);
- $cards = trim($cards, ',');
- $group = $this->dao->select('`group`')->from(TABLE_KANBANLANE)->where('id')->eq($newLaneID)->fetch();
- $waitColumn = $this->dao->select('id')->from(TABLE_KANBANCOLUMN)->where('type')->eq('wait')->andWhere('`group`')->eq($group->group)->fetch();
- if(!empty($waitColumn)) $this->addKanbanCell($kanban->id, $newLaneID, $waitColumn->id, 'task', $cards);
- }
- }
- }
- }
- /**
- * 复制区域中的泳道。
- * Copy lanes.
- *
- * @param object $kanban
- * @param array $copyLanes
- * @param int $regionID
- * @param int $newGroupID
- * @access public
- * @return array|false
- */
- public function copyLanes($kanban, $copyLanes, $regionID, $newGroupID)
- {
- $lanePairs = array();
- foreach($copyLanes as $copyLane)
- {
- if(is_array($copyLane)) $copyLane = (object)$copyLane;
- $laneID = $copyLane->id;
- $copyLane->name = $copyLane->title;
- unset($copyLane->id);
- unset($copyLane->actionList);
- unset($copyLane->title);
- $copyLane->region = $regionID;
- $copyLane->group = $newGroupID;
- $copyLane->lastEditedTime = helper::now();
- $lanePairs[$laneID] = $this->createLane((int)$kanban->id, $regionID, $copyLane, 'copy');
- if(dao::isError()) return false;
- }
- return $lanePairs;
- }
- /**
- * 复制区域中的列。
- * Copy columns.
- *
- * @param array $copyColumns
- * @param int $regionID
- * @param int $newGroupID
- * @param string $from
- * @access public
- * @return void
- */
- public function copyColumns($copyColumns, $regionID, $newGroupID, $from = 'kanban')
- {
- $parentColumns = array();
- foreach($copyColumns as $copyColumn)
- {
- if(is_array($copyColumn)) $copyColumn = (object)$copyColumn;
- $copyColumnID = $copyColumn->id;
- $copyColumn->name = $copyColumn->title;
- unset($copyColumn->id);
- unset($copyColumn->title);
- unset($copyColumn->actionList);
- unset($copyColumn->asParent);
- unset($copyColumn->parentName);
- $copyColumn->region = $regionID;
- $copyColumn->group = $newGroupID;
- if($copyColumn->parent > 0 && isset($parentColumns[$copyColumn->parent]))
- {
- $copyColumn->parent = $parentColumns[$copyColumn->parent];
- }
- $parentColumnID = $this->createColumn($regionID, $copyColumn, $from, 'copy');
- if($copyColumn->parent < 0) $parentColumns[$copyColumnID] = $parentColumnID;
- if(dao::isError()) return false;
- }
- }
- /**
- * 创建默认泳道。
- * Create default lane.
- *
- * @param int $regionID
- * @param int $groupID
- * @access public
- * @return int
- */
- public function createDefaultLane($regionID, $groupID)
- {
- $lane = new stdclass();
- $lane->name = $this->lang->kanbanlane->default;
- $lane->group = $groupID;
- $lane->region = $regionID;
- $lane->type = 'common';
- $lane->lastEditedTime = helper::now();
- $lane->color = '#7ec5ff';
- $lane->order = 1;
- $lane->groupby = '';
- $lane->extra = '';
- $this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec();
- $laneID = $this->dao->lastInsertId();
- return $laneID;
- }
- /**
- * 创建默认看板列。
- * Create default kanban columns.
- *
- * @param int $regionID
- * @param int $groupID
- * @access public
- * @return void
- */
- public function createDefaultColumns($regionID, $groupID)
- {
- $order = 1;
- foreach($this->lang->kanban->defaultColumn as $columnName)
- {
- $column = new stdclass();
- $column->region = $regionID;
- $column->group = $groupID;
- $column->name = $columnName;
- $column->order = $order;
- $column->limit = -1;
- $column->color = '#333';
- $column->type = '';
- $this->createColumn($regionID, $column, 'kanban', 'copy');
- $order ++;
- }
- return !dao::isError();
- }
- /**
- * 创建看板列。
- * Create a column.
- *
- * @param int $regionID
- * @param object $column
- * @param string $from kanban|execution
- * @param string $mode new|copy
- * @access public
- * @return int|false
- */
- public function createColumn($regionID, $column = null, $from = 'kanban', $mode = 'new')
- {
- if($mode == 'new')
- {
- if(!$column->limit && empty($column->noLimit)) dao::$errors['limit'][] = sprintf($this->lang->error->notempty, $this->lang->kanban->WIP);
- if(!preg_match("/^-?\d+$/", (string)$column->limit) || (!isset($column->noLimit) && $column->limit <= 0)) dao::$errors['limit'] = $this->lang->kanban->error->mustBeInt;
- if(dao::isError()) return false;
- }
- $limit = $column->limit;
- if(isset($column->parent) && $column->parent > 0)
- {
- /* Create a child column. */
- $parentColumn = $this->getColumnByID((int)$column->parent);
- if($parentColumn->limit != -1)
- {
- /* The WIP of the child column is infinite or greater than the WIP of the parent column. */
- $sumChildLimit = $this->dao->select('SUM(`limit`) AS sumChildLimit')->from(TABLE_KANBANCOLUMN)->where('parent')->eq($column->parent)->andWhere('deleted')->eq(0)->fetch('sumChildLimit');
- if($limit == -1 || (((int)$limit + (int)$sumChildLimit) > $parentColumn->limit)) dao::$errors['limit'][] = $this->lang->kanban->error->parentLimitNote;
- if(dao::isError()) return false;
- }
- }
- if($mode == 'new' && $column->order)
- {
- $this->dao->update(TABLE_KANBANCOLUMN)
- ->set('`order` = `order` + 1')
- ->where('`group`')->eq($column->group)
- ->andWhere('`order`')->ge($column->order)
- ->exec();
- }
- $this->dao->insert(TABLE_KANBANCOLUMN)->data($column, 'noLimit,position,copyItems')
- ->batchCheck($this->config->kanban->require->createcolumn, 'notempty')
- ->autoCheck()
- ->exec();
- if(dao::isError()) return false;
- $columnID = $this->dao->lastInsertID();
- $this->loadModel('action')->create('kanbanColumn', $columnID, 'Created');
- if($from == 'kanban') $this->dao->update(TABLE_KANBANCOLUMN)->set('type')->eq("column{$columnID}")->where('id')->eq($columnID)->exec();
- /* Add kanban cell. */
- $lanes = $this->dao->select('id,type')->from(TABLE_KANBANLANE)->where('`group`')->eq($column->group)->fetchPairs();
- $kanbanID = $this->dao->select('kanban')->from(TABLE_KANBANREGION)->where('id')->eq($regionID)->fetch('kanban');
- foreach($lanes as $laneID => $laneType) $this->addKanbanCell((int)$kanbanID, $laneID, $columnID, $laneType);
- return $columnID;
- }
- /**
- * 拆分看板列。
- * Split column.
- *
- * @param int $columnID
- * @param array $columns
- * @access public
- * @return void
- */
- public function splitColumn($columnID, $columns)
- {
- $this->loadModel('action');
- $column = $this->getColumnByID($columnID);
- $maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBANCOLUMN)->where('`group`')->eq($column->group)->fetch('maxOrder');
- $order = $maxOrder ? $maxOrder + 1 : 1;
- $sumChildLimit = 0;
- $childrenColumn = array();
- foreach($columns as $childColumn)
- {
- $childColumn->parent = $column->id;
- $childColumn->region = $column->region;
- $childColumn->group = $column->group;
- $childColumn->limit = $childColumn->noLimit == -1 ? -1 : $childColumn->limit;
- $childColumn->order = $order;
- $sumChildLimit += $childColumn->limit == -1 ? 0 : $childColumn->limit;
- if(!$this->checkChildColumn($column, $childColumn, $sumChildLimit)) return false;
- $order ++;
- $childrenColumn[] = $childColumn;
- }
- foreach($childrenColumn as $i => $childColumn)
- {
- $this->dao->insert(TABLE_KANBANCOLUMN)->data($childColumn, 'noLimit')
- ->autoCheck()
- ->batchCheck($this->config->kanban->splitcolumn->requiredFields, 'notempty')
- ->exec();
- if(dao::isError()) return false;
- $childColumnID = $this->dao->lastInsertID();
- $this->dao->update(TABLE_KANBANCOLUMN)->set('type')->eq("column{$childColumnID}")->where('id')->eq($childColumnID)->exec();
- $this->action->create('kanbanColumn', $childColumnID, 'created');
- $this->kanbanTao->addChildColumnCell($columnID, $childColumnID, $i);
- }
- $this->dao->update(TABLE_KANBANCOLUMN)->set('parent')->eq(-1)->where('id')->eq($columnID)->exec();
- }
- /**
- * 检查看板列信息是否合法。
- * Check the column information is legal.
- *
- * @param object $column
- * @param object $childColumn
- * @param int $sumChildLimit
- * @access public
- * @return bool
- */
- public function checkChildColumn($column, $childColumn, $sumChildLimit)
- {
- if(empty($childColumn->name))
- {
- dao::$errors['name'] = sprintf($this->lang->error->notempty, $this->lang->kanbancolumn->name);
- return false;
- }
- if(!preg_match("/^-?\d+$/", (string)$childColumn->limit) || (!$childColumn->noLimit && $childColumn->limit <= 0))
- {
- dao::$errors['limit'] = $this->lang->kanban->error->mustBeInt;
- return false;
- }
- if($column->limit != -1 && ($childColumn->limit == -1 || ($column->limit < $sumChildLimit)))
- {
- dao::$errors['limit'] = $this->lang->kanban->error->parentLimitNote;
- return false;
- }
- return true;
- }
- /**
- * 创建看板卡片。
- * Create a kanban card.
- *
- * @param int $columnID
- * @param object $card
- * @access public
- * @return bool|int
- */
- public function createCard($columnID, $card)
- {
- if($card->estimate < 0)
- {
- dao::$errors['estimate'] = $this->lang->kanbancard->error->recordMinus;
- return false;
- }
- if($card->end && $card->begin > $card->end)
- {
- dao::$errors['end'] = $this->lang->kanbancard->error->endSmall;
- return false;
- }
- $card = $this->loadModel('file')->processImgURL($card, $this->config->kanban->editor->createcard['id'], $this->post->uid);
- $this->dao->insert(TABLE_KANBANCARD)->data($card)->autoCheck()
- ->checkIF($card->estimate != '', 'estimate', 'float')
- ->batchCheck($this->config->kanban->createcard->requiredFields, 'notempty')
- ->exec();
- if(dao::isError()) return false;
- $cardID = $this->dao->lastInsertID();
- $this->loadModel('action')->create('kanbanCard', $cardID, 'created');
- $this->file->saveUpload('kanbancard', $cardID);
- $this->file->updateObjectID($this->post->uid, $cardID, 'kanbancard');
- $this->addKanbanCell((int)$card->kanban, (int)$this->post->lane, $columnID, 'common', (string)$cardID);
- return $cardID;
- }
- /**
- * 转入其它看板的卡片。
- * Import card.
- *
- * @param int $kanbanID
- * @param int $regionID
- * @param int $groupID
- * @param int $columnID
- * @access public
- * @return array|false
- */
- public function importCard($kanbanID, $regionID, $groupID, $columnID)
- {
- $importIDList = $this->post->cards;
- $targetLaneID = $this->post->targetLane;
- if(!$importIDList || !$targetLaneID) return false;
- $updateData = new stdclass();
- $updateData->kanban = $kanbanID;
- $updateData->region = $regionID;
- $updateData->group = $groupID;
- $this->dao->update(TABLE_KANBANCARD)->data($updateData)->where('id')->in($importIDList)->exec();
- $kanban = $this->getByID($kanbanID);
- $oldCardsKanban = array();
- $kanbanUsers = trim($kanban->owner) . ',' . trim($kanban->team);
- $users = $this->loadModel('user')->getPairs('noclosed|nodeleted', '', 0, $kanbanUsers);
- $cardList = $this->dao->select('*')->from(TABLE_KANBANCARD)->where('id')->in($importIDList)->fetchAll('id');
- foreach($cardList as $cardID => $card)
- {
- $oldCardsKanban[$cardID] = $card->kanban;
- if(empty($card->assignedTo)) continue;
- $this->kanbanTao->updateCardAssignedTo($cardID, $card->assignedTo, $users);
- }
- if(!dao::isError())
- {
- $this->removeKanbanCell('common', $importIDList, $oldCardsKanban);
- $cards = implode(',', $importIDList);
- $this->addKanbanCell($kanbanID, (int)$targetLaneID, $columnID, 'common', $cards);
- return $importIDList;
- }
- return false;
- }
- /**
- * 转入其它对象作为看板卡片
- * Import object.
- *
- * @param int $kanbanID
- * @param int $regionID
- * @param int $groupID
- * @param int $columnID
- * @param string $objectType
- * @access public
- * @return array|false
- */
- public function importObject($kanbanID, $regionID, $groupID, $columnID, $objectType)
- {
- $objectIDList = $this->post->{$objectType . 's'};
- $targetLaneID = $this->post->targetLane;
- $objectCards = array();
- $now = helper::now();
- foreach($objectIDList as $objectID)
- {
- $cardData = new stdclass();
- $cardData->kanban = $kanbanID;
- $cardData->region = $regionID;
- $cardData->group = $groupID;
- $cardData->fromID = $objectID;
- $cardData->fromType = $objectType;
- $cardData->createdBy = $this->app->user->account;
- $cardData->createdDate = $now;
- $this->dao->insert(TABLE_KANBANCARD)->data($cardData)->exec();
- $cardID = $this->dao->lastInsertID();
- $objectCards[$cardID] = $objectID;
- }
- if(!dao::isError())
- {
- $cards = implode(',', array_keys($objectCards));
- $this->addKanbanCell($kanbanID, (int)$targetLaneID, $columnID, 'common', $cards);
- return $objectCards;
- }
- return false;
- }
- /**
- * 批量创建卡片。
- * Batch create kanban cards.
- *
- * @param int $kanbanID
- * @param int $regionID
- * @param int $groupID
- * @param int $columnID
- * @param array $cards
- * @access public
- * @return void
- */
- public function batchCreateCard($kanbanID, $regionID, $groupID, $columnID, $cards)
- {
- $now = helper::now();
- foreach($cards as $i => $card)
- {
- if($card->estimate < 0)
- {
- dao::$errors["estimate[$i]"] = $this->lang->kanbancard->error->recordMinus;
- return false;
- }
- if($card->end && $card->begin > $card->end)
- {
- dao::$errors["end[$i]"] = $this->lang->kanbancard->error->endSmall;
- return false;
- }
- }
- foreach($cards as $card)
- {
- $card->kanban = $kanbanID;
- $card->region = $regionID;
- $card->group = $groupID;
- $card->createdBy = $this->app->user->account;
- $card->createdDate = $now;
- $card->assignedDate = $now;
- $card->color = '#fff';
- $this->dao->insert(TABLE_KANBANCARD)->data($card, 'lane')->autoCheck()
- ->checkIF($card->estimate != '', 'estimate', 'float')
- ->batchCheck($this->config->kanban->createcard->requiredFields, 'notempty')
- ->exec();
- if(!dao::isError())
- {
- $cardID = $this->dao->lastInsertID();
- $this->addKanbanCell($kanbanID, $card->lane, $columnID, 'common', (string)$cardID);
- $this->loadModel('action')->create('kanbancard', $cardID, 'created');
- }
- }
- }
- /**
- * 获取看板数据。
- * Get kanban by id.
- *
- * @param int $kanbanID
- * @access public
- * @return object
- */
- public function getByID($kanbanID)
- {
- $kanban = $this->dao->findByID($kanbanID)->from(TABLE_KANBAN)->fetch();
- $kanban = $this->loadModel('file')->replaceImgURL($kanban, 'desc');
- return $kanban;
- }
- /**
- * 获取看板键值对。
- * Get kanban pairs.
- *
- * @access public
- * @return array
- */
- public function getPairs()
- {
- $idList = $this->getCanViewObjects();
- return $this->dao->select('id,name')->from(TABLE_KANBAN)
- ->where('id')->in($idList)
- ->andWhere('deleted')->eq('0')
- ->fetchPairs();
- }
- /**
- * 获取看板所有数据。
- * Get kanban data.
- *
- * @param int $kanbanID
- * @param mixed $regionIDList
- * @access public
- * @return array
- */
- public function getKanbanData($kanbanID, $regionIDList = '')
- {
- $regions = $this->getRegionPairs($kanbanID);
- if(empty($regionIDList))
- {
- $regionIDList = array_keys($regions);
- }
- elseif(!is_array($regionIDList))
- {
- $regionIDList = array($regionIDList);
- }
- $groupGroup = $this->getGroupGroupByRegions($regionIDList);
- $laneGroup = $this->getLaneGroupByRegions($regionIDList);
- $columnGroup = $this->getColumnGroupByRegions($regionIDList);
- $cardGroup = $this->getCardGroupByKanban($kanbanID);
- $kanbanList = array();
- foreach($regionIDList as $regionID)
- {
- $regionData = array();
- $heading = new stdclass();
- $heading->title = new stdClass();
- $heading->title->html = zget($regions, $regionID, '') . '<span class="icon icon-angle-top"></span>';
- $heading->actions = $this->getRegionActions($kanbanID, $regionID, count($regions));
- $regionData['key'] = "region{$regionID}";
- $regionData['id'] = $regionID;
- $regionData['heading'] = $heading;
- $regionData['toggleFromHeading'] = true;
- $groups = zget($groupGroup, $regionID, array());
- $kanbanList[] = $this->kanbanTao->buildRegionData($regionData, $groups, $laneGroup, $columnGroup, $cardGroup);
- }
- return $kanbanList;
- }
- /**
- * 获取看板区域上的操作按钮。
- * Get region actions.
- *
- * @param int $kanbanID
- * @param int|string $regionID
- * @param int $regionCount
- * @access public
- * @return array
- */
- public function getRegionActions($kanbanID, $regionID, $regionCount)
- {
- $action = array();
- $actions = array();
- $action['type'] = 'dropdown';
- $action['icon'] = 'ellipsis-v';
- $action['caret'] = false;
- $action['items'] = array();
- if(common::hasPriv('kanban', 'createRegion')) $action['items'][] = array('text' => $this->lang->kanban->createRegion, 'url' => helper::createLink('kanban', 'createRegion', "kanbanID=$kanbanID"), 'data-toggle' => 'modal', 'icon' => 'plus');
- if(common::hasPriv('kanban', 'editRegion')) $action['items'][] = array('text' => $this->lang->kanban->editRegion, 'url' => helper::createLink('kanban', 'editRegion', "regionID=$regionID"), 'data-toggle' => 'modal', 'icon' => 'edit');
- if(common::hasPriv('kanban', 'sortRegion')) $action['items'][] = array('text' => $this->lang->kanban->sortRegion, 'url' => 'javascript:;', 'icon' => 'move', 'data-on' => 'click', 'data-call' => 'sortItems', 'data-params' => 'event', 'data-type' => 'region', 'data-id' => $regionID);
- if(common::hasPriv('kanban', 'createLane')) $action['items'][] = array('text' => $this->lang->kanban->createLane, 'url' => helper::createLink('kanban', 'createLane', "kanbanID=$kanbanID®ionID=$regionID"), 'data-toggle' => 'modal', 'icon' => 'plus');
- if(common::hasPriv('kanban', 'deleteRegion') and ($regionCount > 1)) $action['items'][] = array('text' => $this->lang->kanban->deleteRegion, 'url' => helper::createLink('kanban', 'deleteRegion', "regionID=$regionID"), 'data-confirm' => $this->lang->kanbanregion->confirmDelete, 'icon' => 'trash', 'innerClass' => 'ajax-submit');
- $action['items'][] = array('type' => 'divider');
- if(commonModel::hasPriv('kanban', 'viewArchivedCard')) $action['items'][] = array('text' => $this->lang->kanban->viewArchivedCard, 'url' => "javascript:loadMore(\"Card\", $regionID)", 'icon' => 'card-archive');
- if(commonModel::hasPriv('kanban', 'viewArchivedColumn')) $action['items'][] = array('text' => $this->lang->kanban->viewArchivedColumn, 'url' => "javascript:loadMore(\"Column\", $regionID)", 'icon' => 'col-archive');
- $actions[] = $action;
- return $actions;
- }
- /**
- * 获取专业研发看板区域上的操作按钮。
- * Get RD region actions.
- *
- * @param int $kanbanID
- * @param int $regionID
- * @param int $regionCount
- * @access public
- * @return array
- */
- public function getRDRegionActions($kanbanID, $regionID, $regionCount = 1)
- {
- $action = array();
- $actions = array();
- $action['type'] = 'dropdown';
- $action['icon'] = 'ellipsis-v';
- $action['caret'] = false;
- $action['items'] = array();
- if(common::hasPriv('kanban', 'createRegion')) $action['items'][] = array('text' => $this->lang->kanban->createRegion, 'url' => helper::createLink('kanban', 'createRegion', "kanbanID=$kanbanID&from=execution"), 'data-toggle' => 'modal', 'icon' => 'plus', 'class' => 'kanban-createRegion-btn');
- if(common::hasPriv('kanban', 'editRegion')) $action['items'][] = array('text' => $this->lang->kanban->editRegion, 'url' => helper::createLink('kanban', 'editRegion', "regionID=$regionID"), 'data-toggle' => 'modal', 'icon' => 'edit');
- if(common::hasPriv('kanban', 'createLane')) $action['items'][] = array('text' => $this->lang->kanban->createLane, 'url' => helper::createLink('kanban', 'createLane', "kanbanID=$kanbanID®ionID=$regionID&from=execution"), 'data-toggle' => 'modal', 'icon' => 'plus');
- if(common::hasPriv('kanban', 'deleteRegion') && ($regionCount > 1)) $action['items'][] = array('text' => $this->lang->kanban->deleteRegion, 'url' => helper::createLink('kanban', 'deleteRegion', "regionID=$regionID"), 'data-confirm' => $this->lang->kanbanregion->confirmDelete, 'icon' => 'trash', 'innerClass' => 'ajax-submit');
- $actions[] = $action;
- return $actions;
- }
- /**
- * 获取计划看板。
- * Get plan kanban.
- *
- * @param object $product
- * @param string $branchID
- * @param array $planGroup
- * @access public
- * @return array
- */
- public function getPlanKanban($product, $branchID = '0', $planGroup = array())
- {
- $this->loadModel('productplan');
- $lanes = $columns = $kanbanData = $planList = $lanes = $columns = $columnCards = array();
- $colorIndex = 0;
- $laneOrder = 1;
- $cardActions = array('view', 'createExecution', 'linkStory', 'linkBug', 'edit', 'start', 'finish', 'close', 'activate', 'delete');
- $branches = $this->kanbanTao->getBranchesForPlanKanban($product, $branchID);
- foreach($branches as $id => $name)
- {
- if($product->type != 'normal') $plans = isset($planGroup[$product->id][$id]) ? array_filter($planGroup[$product->id][$id]) : array();
- if($product->type == 'normal') $plans = $planGroup;
- foreach($plans as $plan)
- {
- if(empty($plan) || $plan->parent == -1) continue;
- $plan->isParent = false;
- $planBegin = $plan->begin == $this->config->productplan->future ? $this->lang->productplan->future : date('m-d', strtotime($plan->begin));
- $planEnd = $plan->end == $this->config->productplan->future ? $this->lang->productplan->future : date('m-d', strtotime($plan->end));
- $item = array('id' => $plan->id, 'name' => $plan->id, 'title' => htmlspecialchars_decode($plan->title), 'status' => $plan->status);
- $item['statusLabel'] = zget($this->lang->productplan->statusList, $plan->status);
- $item['delay'] = helper::today() > $plan->end ? true : false;
- $item['desc'] = strip_tags(htmlspecialchars_decode($plan->desc));
- $item['dateLine'] = in_array($this->lang->productplan->future, array($planBegin, $planEnd)) ? $this->lang->productplan->future : $planBegin . ' ' . $this->lang->productplan->to . ' ' . $planEnd;
- $item['actionList'] = array();
- foreach($cardActions as $action)
- {
- if($this->productplan->isClickable($plan, $action)) $item['actionList'][] = $action;
- }
- $planList['lane' . $id][$plan->status][] = $item;
- if(!isset($columnCards[$plan->status])) $columnCards[$plan->status] = 0;
- $columnCards[$plan->status] ++;
- }
- $lanes[] = array('id' => $id, 'name' => 'lane' . $id, 'title' => $name, 'color' => $this->config->productplan->laneColorList[$colorIndex], 'order' => $laneOrder);
- $laneOrder ++;
- $colorIndex ++;
- if($colorIndex == count($this->config->productplan->laneColorList)) $colorIndex = 0;
- }
- foreach($this->lang->kanban->defaultColumn as $columnType => $columnName) $columns[] = array('id' => $columnType, 'name' => $columnType, 'title' => $columnName);
- foreach($columns as $key => $column) $columns[$key]['cards'] = !empty($columnCards[$column['name']]) ? $columnCards[$column['name']] : 0;
- $groupData['key'] = 'planKanban';
- $groupData['data'] = array('lanes' => $lanes, 'cols' => $columns, 'items' => $planList);
- $kanbanData[] = array('items' => array($groupData), 'key' => 'planKanban');
- return $kanbanData;
- }
- /**
- * 获取专业研发看板。
- * Get a RD kanban data.
- *
- * @param int $executionID
- * @param string $browseType all|story|task|bug
- * @param string $orderBy
- * @param int $regionID
- * @param string $groupBy
- * @param string $searchValue
- * @access public
- * @return array
- */
- public function getRDKanban($executionID, $browseType = 'all', $orderBy = 'id_desc', $regionID = 0, $groupBy = 'default', $searchValue = '')
- {
- $kanbanList = array();
- $execution = $this->loadModel('execution')->getByID($executionID);
- if($groupBy != 'default' && $groupBy != '') return $this->getRDKanbanByGroup($execution, $browseType, $orderBy, $regionID, $groupBy, $searchValue);
- $regions = $this->getRegionPairs($executionID, $regionID, 'execution');
- $regionIDList = $regionID == 0 ? array_keys($regions) : array(0 => $regionID);
- $groupGroup = $this->getGroupGroupByRegions($regionIDList);
- $laneGroup = $this->getLaneGroupByRegions($regionIDList, $browseType);
- foreach($laneGroup as $lanes)
- {
- foreach($lanes as $lane)
- {
- $lane['execution'] = $executionID;
- if(in_array($execution->attribute, array('request', 'design', 'review')) && $lane['type'] == 'bug') continue 2;
- if(in_array($execution->attribute, array('request', 'review')) && $lane['type'] == 'story') continue 2;
- $this->refreshCards((array)$lane);
- $lane['defaultCardType'] = $lane['type'];
- }
- }
- $columnGroup = $this->getRDColumnGroupByRegions($regionIDList, array_keys($laneGroup));
- $cardGroup = $this->getCardGroupByExecution($executionID, $browseType, $orderBy, $searchValue);
- foreach($regions as $regionID => $regionName)
- {
- $regionData = array();
- $heading = new stdclass();
- $heading->title = new stdClass();
- $heading->title->html = $regionName . '<span class="icon icon-angle-top"></span>';
- $heading->actions = $this->getRDRegionActions($executionID, $regionID, count($regions));
- $regionData['key'] = "region{$regionID}";
- $regionData['id'] = $regionID;
- $regionData['heading'] = $heading;
- $regionData['toggleFromHeading'] = true;
- $regionData['items'] = array();
- $groups = zget($groupGroup, $regionID, array());
- $regionData = $this->kanbanTao->buildRDRegionData($regionData, $groups, $laneGroup, $columnGroup, $cardGroup, $searchValue);
- $kanbanList[] = $regionData;
- }
- return $kanbanList;
- }
- /**
- * 获取专业研发看板分组视图下的看板数据。
- * Get kanban data for group view of RD kanban.
- *
- * @param object $execution
- * @param string $browseType
- * @param string $groupBy
- * @param string $searchValue
- * @param string $orderBy
- * @access private
- * @return array
- * @param int $regionID
- */
- private function getRDKanbanByGroup($execution, $browseType, $orderBy, $regionID, $groupBy, $searchValue)
- {
- $regionData = array();
- $heading = new stdclass();
- $heading->title = $execution->name;
- $heading->actions = $this->getRDRegionActions($execution->id, $regionID, 1);
- $regionData['key'] = "region{$execution->id}";
- $regionData['id'] = $execution->id;
- $regionData['heading'] = $heading;
- $regionData['toggleFromHeading'] = true;
- $regionData['items'] = $this->getKanban4Group($execution->id, $browseType, $groupBy, $searchValue, $orderBy);
- $kanbanList[] = $regionData;
- return $kanbanList;
- }
- /**
- * 获取看板区域。
- * Get region by id.
- *
- * @param int $regionID
- * @access public
- * @return object
- */
- public function getRegionByID($regionID)
- {
- return $this->dao->findByID($regionID)->from(TABLE_KANBANREGION)->fetch();
- }
- /**
- * 获取看板区域的键值对。
- * Get ordered region pairs.
- *
- * @param int $kanbanID
- * @param int $regionID
- * @param string $from kanban|execution
- * @access public
- * @return array
- */
- public function getRegionPairs($kanbanID, $regionID = 0, $from = 'kanban')
- {
- if(common::isTutorialMode()) return $this->loadModel('tutorial')->getRegionPairs();
- return $this->dao->select('id,name')->from(TABLE_KANBANREGION)
- ->where('kanban')->eq($kanbanID)
- ->andWhere('deleted')->eq('0')
- ->beginIF($regionID)->andWhere('id')->eq($regionID)->fi()
- ->beginIF($from == 'execution')->andWhere('space')->eq(0)->fi()
- ->beginIF($from == 'kanban')->andWhere('space')->ne(0)->fi()
- ->orderBy('order_asc')
- ->fetchPairs();
- }
- /**
- * 根据区域获取看板ID。
- * Get kanban id by region id.
- *
- * @param int $regionID
- * @access public
- * @return int
- */
- public function getKanbanIDByRegion($regionID)
- {
- return (int)$this->dao->select('kanban')->from(TABLE_KANBANREGION)->where('id')->eq($regionID)->fetch('kanban');
- }
- /**
- * 分组获取看板组。
- * Get kanban group by regions.
- *
- * @param array $regions
- * @access public
- * @return array
- */
- public function getGroupGroupByRegions($regions)
- {
- if(common::isTutorialMode()) return $this->loadModel('tutorial')->getGroups();
- return $this->dao->select('*')->from(TABLE_KANBANGROUP)
- ->where('region')->in($regions)
- ->orderBy('order')
- ->fetchGroup('region', 'id');
- }
- /**
- * 获取区域中的泳道分组。
- * Get lane group by regions.
- *
- * @param array $regions
- * @param string $browseType
- * @access public
- * @return array
- */
- public function getLaneGroupByRegions($regions, $browseType = 'all')
- {
- if(common::isTutorialMode()) return $this->loadModel('tutorial')->getLaneGroup();
- $lanes = $this->dao->select('*')->from(TABLE_KANBANLANE)
- ->where('deleted')->eq('0')
- ->andWhere('region')->in($regions)
- ->beginIf($browseType != 'all')->andWhere('type')->eq($browseType)->fi()
- ->orderBy('order_asc')
- ->fetchAll();
- $actions = array('sortLane', 'deleteLane', 'editLaneName', 'editLaneColor');
- $laneGroup = array();
- foreach($lanes as $lane)
- {
- $item = array();
- $item['id'] = $lane->id;
- $item['execution'] = $lane->execution;
- $item['type'] = $lane->type;
- $item['name'] = $lane->id;
- $item['region'] = $lane->region;
- $item['title'] = htmlspecialchars_decode($lane->name);
- $item['color'] = $lane->color;
- $item['order'] = $lane->order;
- foreach($actions as $action)
- {
- if($this->isClickable($lane, $action)) $item['actionList'][] = $action;
- }
- $laneGroup[$lane->group][] = $item;
- }
- return $laneGroup;
- }
- /**
- * 根据分组ID获取泳道键值对。
- * Get kanban lane pairs by group id.
- *
- * @param int $groupID
- * @param string $orderBy
- * @access public
- * @return array
- */
- public function getLanePairsByGroup($groupID, $orderBy = '`order`_asc')
- {
- return $this->dao->select('id,name')->from(TABLE_KANBANLANE)
- ->where('deleted')->eq(0)
- ->andWhere('`group`')->eq($groupID)
- ->orderBy($orderBy)
- ->fetchPairs();
- }
- /**
- * 根据分组ID获取看板列键值对。
- * Get kanban column pairs by group id.
- *
- * @param int $groupID
- * @param int $parentID
- * @param string $orderBy
- * @access public
- * @return array
- */
- public function getColumnPairsByGroup($groupID, $parentID = 0, $orderBy = '`order`_asc')
- {
- return $this->dao->select('id,name')->from(TABLE_KANBANCOLUMN)
- ->where('deleted')->eq(0)
- ->andWhere('`group`')->eq($groupID)
- ->beginIf($parentID > 0)->andWhere('parent')->eq($parentID)->fi()
- ->beginIf($parentID <= 0)->andWhere('parent')->le(0)->fi()
- ->orderBy($orderBy)
- ->fetchPairs();
- }
- /**
- * Get column group by regions.
- *
- * @param array $regions
- * @param string $order order|id_asc
- * @param string $param
- * @access public
- * @return array
- */
- public function getColumnGroupByRegions($regions, $order = '`order`', $param = '')
- {
- $columns = $this->dao->select("*")->from(TABLE_KANBANCOLUMN)
- ->where('deleted')->eq('0')
- ->andWhere('region')->in($regions)
- ->beginIF(strpos(",$param,", ',withArchived,') === false)->andWhere('archived')->eq('0')->fi()
- ->orderBy($order)
- ->fetchAll();
- $actions = array('createColumn', 'setColumn', 'setWIP', 'sortColumn', 'archiveColumn', 'restoreColumn', 'deleteColumn', 'createCard', 'batchCreateCard', 'splitColumn', 'sortColumn');
- /* Group by parent. */
- $columnGroup = array();
- foreach($columns as $column)
- {
- $item = array();
- $item['title'] = htmlspecialchars_decode($column->name);
- $item['name'] = $column->id;
- $item['id'] = $column->id;
- $item['type'] = $column->type;
- $item['limit'] = $column->limit;
- $item['region'] = $column->region;
- $item['group'] = $column->group;
- $item['parent'] = $column->parent;
- $item['color'] = $column->color;
- $item['order'] = (int)$column->order;
- if($column->parent > 0) $item['parentName'] = $column->parent;
- /* Judge column action priv. */
- foreach($actions as $action)
- {
- if($this->isClickable($column, $action)) $item['actionList'][] = $action;
- }
- $columnGroup[$column->group][] = $item;
- }
- return $columnGroup;
- }
- /**
- * 获取看板下的卡片。
- * Get card group by kanban id.
- *
- * @param int $kanbanID
- * @access public
- * @return array
- */
- public function getCardGroupByKanban($kanbanID)
- {
- /* Get card data.*/
- $cards = $this->dao->select('*')->from(TABLE_KANBANCARD)
- ->where('deleted')->eq(0)
- ->andWhere('kanban')->eq($kanbanID)
- ->andWhere('archived')->eq(0)
- ->andWhere('fromID')->eq(0)
- ->fetchAll('id', false);
- foreach($this->config->kanban->fromType as $fromType) $cards = $this->getImportedCards($kanbanID, $cards, $fromType);
- $cellList = $this->dao->select('*')->from(TABLE_KANBANCELL)
- ->where('kanban')->eq($kanbanID)
- ->andWhere('type')->eq('common')
- ->fetchAll('id', false);
- $actions = array('editCard', 'archiveCard', 'deleteCard', 'moveCard', 'setCardColor', 'viewCard', 'sortCard', 'viewExecution', 'viewPlan', 'viewRelease', 'viewBuild', 'viewTicket', 'activateCard', 'finishCard');
- $cardGroup = array();
- $avatarPairs = $this->loadModel('user')->getAvatarPairs();
- $users = $this->loadModel('user')->getPairs('noletter');
- foreach($cellList as $cell)
- {
- $cardIdList = array_filter(explode(',', $cell->cards));
- if(empty($cardIdList)) continue;
- $order = 0;
- foreach($cardIdList as $cardID)
- {
- if(!isset($cards[$cardID])) continue;
- $card = zget($cards, $cardID);
- $item = $this->kanbanTao->initCardItem($card, $cell, $order, $avatarPairs, $users);
- $order ++;
- foreach($actions as $action)
- {
- if(in_array($action, array('viewExecution', 'viewPlan', 'viewRelease', 'viewBuild', 'viewTicket')))
- {
- if($card->fromType == 'execution')
- {
- if($card->execType == 'kanban' && common::hasPriv('execution', 'kanban')) $item['actionList'][] = $action;
- if($card->execType != 'kanban' && common::hasPriv('execution', 'view')) $item['actionList'][] = $action;
- }
- else
- {
- if(common::hasPriv($fromType, 'view')) $item['actionList'][] = $action;
- }
- continue;
- }
- if(common::hasPriv('kanban', $action)) $item['actionList'][] = $action;
- }
- $cardGroup[$card->group][$cell->lane][$cell->column][] = $item;
- }
- }
- return $cardGroup;
- }
- /**
- * 获取已经导入的卡片。
- * Get imported cards.
- *
- * @param int $kanbanID
- * @param array $cards
- * @param string $fromType
- * @param int $archived
- * @param int $regionID
- * @access public
- * @return array
- */
- public function getImportedCards($kanbanID, $cards, $fromType, $archived = 0, $regionID = 0)
- {
- /* Get imported cards based on imported object type. */
- $objectCards = $this->dao->select('*')->from(TABLE_KANBANCARD)
- ->where('deleted')->eq(0)
- ->andWhere('kanban')->eq($kanbanID)
- ->andWhere('archived')->eq($archived)
- ->andWhere('fromType')->eq($fromType)
- ->beginIF($regionID)->andWhere('region')->eq($regionID)->fi()
- ->fetchGroup('fromID', 'id');
- if(!empty($objectCards))
- {
- /* Get imported objects. */
- $table = $this->config->objectTables[$fromType];
- $objects = $this->dao->select('*')->from($table)
- ->where('id')->in(array_keys($objectCards))
- ->fetchAll('id', false);
- $creators = array();
- if($fromType == 'productplan' || $fromType == 'release')
- {
- $creators = $this->dao->select('objectID, actor')->from(TABLE_ACTION)
- ->where('objectID')->in(array_keys($objectCards))
- ->andWhere('objectType')->eq($fromType)
- ->andWhere('action')->eq('opened')
- ->fetchPairs();
- }
- /* Data for constructing the card. */
- foreach($objectCards as $objectID => $cardsInfo)
- {
- foreach($cardsInfo as $cardID => $objectCard)
- {
- $object = $objects[$objectID];
- $fieldType = $fromType . 'Field';
- if($fromType == 'execution') $objectCard->progress = $object->progress;
- foreach($this->config->kanban->$fieldType as $field) $objectCard->$field = $object->$field;
- $objectCard = $this->kanbanTao->buildObjectCard($objectCard, $object, $fromType, $creators);
- $cards[$cardID] = $objectCard;
- }
- }
- }
- return $cards;
- }
- /**
- * 获取专业研发看板的列。
- * Get RD column group by regions.
- *
- * @param array $regions
- * @param array $groupIDList
- * @access public
- * @return array
- */
- public function getRDColumnGroupByRegions($regions, $groupIDList = array())
- {
- if(common::isTutorialMode()) return $this->loadModel('tutorial')->getColumns();
- $columnGroup = $this->dao->select("*")->from(TABLE_KANBANCOLUMN)
- ->where('deleted')->eq('0')
- ->andWhere('region')->in($regions)
- ->beginIF(!empty($groupIDList))->andWhere('`group`')->in($groupIDList)->fi()
- ->orderBy('id_asc')
- ->fetchGroup('group');
- $actions = array('setColumn', 'setWIP', 'deleteColumn');
- /* Group by parent. */
- $columnData = array();
- foreach($columnGroup as $group => $columns)
- {
- foreach($columns as $column)
- {
- $item = array();
- $item['title'] = htmlspecialchars_decode($column->name);
- $item['name'] = $column->id;
- $item['id'] = $column->id;
- $item['type'] = $column->type;
- $item['limit'] = $column->limit;
- $item['region'] = $column->region;
- $item['group'] = $column->group;
- $item['parent'] = $column->parent;
- $item['color'] = $column->color;
- if($column->parent > 0) $item['parentName'] = $column->parent;
- /* Judge column action priv. */
- foreach($actions as $action)
- {
- if($this->isClickable($column, $action)) $item['actionList'][] = $action;
- }
- $columnData[$group][] = $item;
- }
- }
- return $columnData;
- }
- /**
- * 获取执行中的看板卡片分组。
- * Get card group by execution id.
- *
- * @param int $kanbanID
- * @param string $browseType all|task|bug|story
- * @param string $orderBy
- * @param string $searchValue
- *
- * @access public
- * @return array
- * @param int $executionID
- */
- public function getCardGroupByExecution($executionID, $browseType = 'all', $orderBy = 'id_asc', $searchValue = '')
- {
- if(common::isTutorialMode()) return $this->loadModel('tutorial')->getCardGroup();
- $cards = $this->dao->select('t1.*, t2.type as columnType, t2.group')
- ->from(TABLE_KANBANCELL)->alias('t1')
- ->leftJoin(TABLE_KANBANCOLUMN)->alias('t2')->on('t1.column=t2.id')
- ->where('t1.kanban')->eq($executionID)
- ->beginIF($browseType != 'all')->andWhere('t1.type')->eq($browseType)->fi()
- ->orderby($orderBy)
- ->fetchgroup('lane', 'column');
- /* Get group objects. */
- if($browseType == 'all' || $browseType == 'parentStory') $objectGroup['parentStory'] = $this->loadModel('story')->getExecutionStories($executionID, 0, 't1.`order`_desc', 'allStory');
- if($browseType == 'all' || $browseType == 'story') $objectGroup['story'] = $this->loadModel('story')->getExecutionStories($executionID, 0, 't1.`order`_desc', 'allStory');
- if($browseType == 'all' || $browseType == 'bug') $objectGroup['bug'] = $this->loadModel('bug')->getExecutionBugs($executionID);
- if($browseType == 'all' || $browseType == 'task') $objectGroup['task'] = $this->loadModel('execution')->getKanbanTasks($executionID, "id");
- $cardGroup = array();
- $avatarPairs = $this->loadModel('user')->getAvatarPairs();
- $users = $this->loadModel('user')->getPairs('noletter');
- foreach($cards as $laneID => $cells)
- {
- foreach($cells as $cell)
- {
- $order = 0;
- $cardIdList = array_filter(explode(',', $cell->cards));
- foreach($cardIdList as $cardID)
- {
- $cardData = array();
- $objects = zget($objectGroup, $cell->type, array());
- $object = zget($objects, $cardID, array());
- if(empty($object)) continue;
- if($cell->type == 'parentStory' && $object->isParent != '1') continue;
- if($cell->type == 'story' && $object->isParent == '1') continue;
- $cardData = $this->kanbanTao->initCardItem($object, $cell, $order, $avatarPairs, $users);
- $cardData['acl'] = 'open';
- $order ++;
- if($searchValue != '' && strpos($cardData['title'], $searchValue) === false) continue;
- $cardGroup[$cell->group][$laneID][$cell->column][] = $cardData;
- }
- }
- }
- return $cardGroup;
- }
- /**
- * 获取执行看板的数据。
- * Get Kanban by execution id.
- *
- * @param int $executionID
- * @param string $browseType all|story|bug|task
- * @param string $groupBy default|pri|category|module|source|assignedTo|type|story|severity
- * @param string $searchValue
- * @param string $orderBy
- * @access public
- * @return array
- */
- public function getExecutionKanban($executionID, $browseType = 'all', $groupBy = 'default', $searchValue = '', $orderBy = 'id_asc')
- {
- if($groupBy != 'default') return array($this->getKanban4Group($executionID, $browseType, $groupBy, $searchValue, $orderBy), array());
- $execution = $this->loadModel('execution')->fetchByID($executionID);
- $lanes = $this->dao->select('*')->from(TABLE_KANBANLANE)
- ->where('execution')->eq($executionID)
- ->andWhere('deleted')->eq(0)
- ->beginIF($browseType != 'all')->andWhere('type')->eq($browseType)->fi()
- ->beginIF(!empty($execution->attribute) && !in_array($execution->attribute, array('mix', 'request', 'design')))->andWhere('type')->notIn('epic,requirement')->fi()
- ->orderBy('order_asc')
- ->fetchAll('id');
- if(empty($lanes)) return array();
- foreach($lanes as $lane) $this->refreshCards((array)$lane);
- $columns = $this->dao->select('t1.cards, t1.lane, t2.id, t2.type, t2.name, t2.color, t2.limit, t2.parent')->from(TABLE_KANBANCELL)->alias('t1')
- ->leftJoin(TABLE_KANBANCOLUMN)->alias('t2')->on('t1.column = t2.id')
- ->where('t2.deleted')->eq(0)
- ->andWhere('t1.lane')->in(array_keys($lanes))
- ->orderBy('id_asc')
- ->fetchGroup('lane', 'id');
- $geMax = in_array($this->config->edition, array('max', 'ipd'));
- /* Get group objects. */
- if($browseType == 'all' || $browseType == 'epic') $objectGroup['epic'] = $this->loadModel('story')->getExecutionStories($executionID, 0, 't1.`order`_desc', 'allStory', '0', 'epic');
- if($browseType == 'all' || $browseType == 'requirement') $objectGroup['requirement'] = $this->loadModel('story')->getExecutionStories($executionID, 0, 't1.`order`_desc', 'allStory', '0', 'requirement');
- if($browseType == 'all' || $browseType == 'parentStory') $objectGroup['parentStory'] = $this->loadModel('story')->getExecutionStories($executionID, 0, 't1.`order`_desc', 'allStory');
- if($browseType == 'all' || $browseType == 'story') $objectGroup['story'] = $this->loadModel('story')->getExecutionStories($executionID, 0, 't1.`order`_desc', 'allStory');
- if($browseType == 'all' || $browseType == 'bug') $objectGroup['bug'] = $this->loadModel('bug')->getExecutionBugs($executionID);
- if($browseType == 'all' || $browseType == 'task') $objectGroup['task'] = $this->loadModel('execution')->getKanbanTasks($executionID, "id");
- if($geMax && ($browseType == 'all' || $browseType == 'risk')) $objectGroup['risk'] = $this->loadModel('risk')->getKanbanRisks($executionID);
- /* Get objects cards menus. */
- $menus = array();
- $menus['epic'] = $browseType == 'all' || $browseType == 'epic' ? $this->getKanbanCardMenu($executionID, $objectGroup['epic'], 'epic') : array();
- $menus['requirement'] = $browseType == 'all' || $browseType == 'requirement' ? $this->getKanbanCardMenu($executionID, $objectGroup['requirement'], 'requirement') : array();
- $menus['parentStory'] = $browseType == 'all' || $browseType == 'parentStory' ? $this->getKanbanCardMenu($executionID, $objectGroup['parentStory'], 'parentStory') : array();
- $menus['story'] = $browseType == 'all' || $browseType == 'story' ? $this->getKanbanCardMenu($executionID, $objectGroup['story'], 'story') : array();
- $menus['bug'] = $browseType == 'all' || $browseType == 'bug' ? $this->getKanbanCardMenu($executionID, $objectGroup['bug'], 'bug') : array();
- $menus['task'] = $browseType == 'all' || $browseType == 'task' ? $this->getKanbanCardMenu($executionID, $objectGroup['task'], 'task') : array();
- $menus['risk'] = ($geMax && ($browseType == 'all' || $browseType == 'risk')) ? $this->getKanbanCardMenu($executionID, $objectGroup['risk'], 'risk') : array();
- /* 获取看板连线的fromKanbanID. */
- $storyFromKanbanID = '';
- $taskFromKanbanID = '';
- foreach($lanes as $lane)
- {
- if($lane->type == 'parentStory') $storyFromKanbanID = 'group' . $lane->id;
- if($lane->type == 'task') $taskFromKanbanID = 'group' . $lane->id;
- }
- /* Build kanban group data. */
- $kanbanGroup = array();
- $links = array();
- $execution = $this->loadModel('execution')->fetchById($executionID);
- $project = $this->loadModel('project')->fetchById($execution->project);
- foreach($lanes as $lane)
- {
- $laneType = $lane->type;
- if(in_array($laneType, array('epic', 'requirement')) && strpos($project->storyType, $laneType) === false) continue;
- list($laneData, $columnData, $cardsData) = $this->buildExecutionGroup($lane, $columns, $objectGroup, $searchValue, $menus);
- if($lane->type == 'risk' && in_array($this->config->edition, array('max', 'ipd'))) $cardsData = $this->appendRiskField($cardsData, $objectGroup['risk']);
- $kanbanID = 'group' . $lane->id;
- $kanbanGroup[$laneType]['id'] = $lane->id;
- $kanbanGroup[$laneType]['key'] = $kanbanID;
- $kanbanGroup[$laneType]['data'] = array();
- $kanbanGroup[$laneType]['data']['lanes'] = array($laneData);
- $kanbanGroup[$laneType]['data']['cols'] = $columnData;
- $kanbanGroup[$laneType]['data']['items'] = $cardsData;
- foreach($cardsData as $columnCard)
- {
- foreach($columnCard as $cards)
- {
- foreach($cards as $card)
- {
- if($card['parent'] == 0) continue;
- /* 获取看板卡片的连线关系,业需、用需和父需求共用同一组看板列,所以fromKanban和toKanban是一样的。 */
- $link = array('from' => $card['parent'], 'to' => $card['id']);
- if($laneType == 'task')
- {
- $link['fromKanban'] = $taskFromKanbanID;
- $link['toKanban'] = $taskFromKanbanID;
- }
- else
- {
- $link['fromKanban'] = $storyFromKanbanID;
- $link['toKanban'] = in_array($laneType, array('epic', 'requirement')) ? $storyFromKanbanID : $kanbanID;
- }
- $links[] = $link;
- }
- }
- }
- }
- /* 展示所有泳道的时候,业务、用需与父需求泳道共用看板列。 */
- if($browseType == 'all')
- {
- foreach(array('requirement', 'epic') as $laneType)
- {
- if(!isset($kanbanGroup[$laneType])) continue;
- array_unshift($kanbanGroup['parentStory']['data']['lanes'], $kanbanGroup[$laneType]['data']['lanes'][0]);
- $kanbanGroup['parentStory']['data']['items'] += $kanbanGroup[$laneType]['data']['items'];
- unset($kanbanGroup[$laneType]);
- }
- }
- return array(array_values($kanbanGroup), $links);
- }
- /**
- * 构建迭代看板的泳道组数据。
- * Build the laneGroup data for the execution Kanban.
- *
- * @param object $lane
- * @param array $columns
- * @param array $objectGroup
- * @param string $searchValue
- * @param array $menus array(story => array, bug => array, task => array)
- * @access public
- * @return array
- */
- public function buildExecutionGroup($lane, $columns, $objectGroup, $searchValue = '', $menus = array())
- {
- $laneData = array();
- $columnsData = array();
- $cardsData = array();
- $laneData['id'] = $lane->id;
- $laneData['type'] = $lane->type;
- $laneData['name'] = $lane->id;
- $laneData['region'] = $lane->region;
- $laneData['title'] = $lane->name;
- $laneData['color'] = $lane->color;
- foreach($columns[$lane->id] as $columnID => $column)
- {
- $cardIdList = array_unique(array_filter(explode(',', $column->cards)));
- $columnsData[$column->id]['id'] = $columnID;
- $columnsData[$column->id]['type'] = $column->type;
- $columnsData[$column->id]['name'] = $columnID;
- $columnsData[$column->id]['title'] = $column->name;
- $columnsData[$column->id]['color'] = $column->color;
- $columnsData[$column->id]['limit'] = $column->limit;
- $columnsData[$column->id]['region'] = $lane->region;
- $columnsData[$column->id]['laneName'] = $column->lane;
- $columnsData[$column->id]['group'] = $lane->type;
- $columnsData[$column->id]['cards'] = 0;
- $columnsData[$column->id]['actionList'] = array('setColumn', 'setWIP');
- if($column->parent > 0) $columnsData[$column->id]['parentName'] = $column->parent;
- if($cardIdList)
- {
- $cardsData = $this->buildExecutionCards($cardsData, $column, $lane->type, $cardIdList, $objectGroup, $searchValue, $menus);
- $columnsData[$column->id]['cards'] = empty($cardsData[$column->lane][$column->id]) ? 0 : count($cardsData[$column->lane][$column->id]);
- }
- }
- foreach($columnsData as $columnData)
- {
- if(isset($columnData['parentName'])) $columnsData[$columnData['parentName']]['cards'] += $columnData['cards'];
- }
- return array($laneData, array_values($columnsData), $cardsData);
- }
- /**
- * 构建迭代看板的卡片数据。
- * Build the card data for the execution Kanban.
- *
- * @param object $lane
- * @param array $laneData
- * @param string $columnType
- * @param array $cardIdList
- * @param array $objectGroup
- * @param string $searchValue
- * @param array $menus array(story => array, bug => array, task => array)
- * @access public
- * @return array
- * @param object $column
- * @param mixed[] $cardsData
- * @param string $laneType
- */
- public function buildExecutionCards($cardsData, $column, $laneType, $cardIdList, $objectGroup, $searchValue = '', $menus = array())
- {
- foreach($cardIdList as $cardID)
- {
- $cardData = array();
- $objects = zget($objectGroup, $laneType, array());
- $object = zget($objects, $cardID, array());
- if(empty($object)) continue;
- if($laneType == 'story' && $object->isParent == '1') continue;
- $cardData = $this->buildExecutionCard($object, $column, $laneType, $searchValue, $menus);
- if(empty($cardData)) continue;
- $cardsData[$column->lane][$column->id][] = $cardData;
- }
- return $cardsData;
- }
- /**
- * 构造执行看板卡片数据。
- * Build execution card.
- *
- * @param object $object
- * @param object $column
- * @param string $laneType
- * @param string $searchValue
- * @param array $menus array(story => array, bug => array, task => array)
- * @access public
- * @return array
- */
- public function buildExecutionCard($object, $column, $laneType, $searchValue = '', $menus = array())
- {
- if(empty($object)) return array();
- $cardData = array();
- $cardData['id'] = $object->id;
- $cardData['lane'] = $column->lane;
- $cardData['column'] = zget($column, 'column', $column->id);
- $cardData['pri'] = zget($object, 'pri', 0);
- $cardData['group'] = $laneType;
- $cardData['parent'] = zget($object, 'originParent', 0);
- $cardData['status'] = zget($object, 'status', '');
- $cardData['estimate'] = zget($object, 'estimate', 0);
- $cardData['assignedTo'] = $object->assignedTo;
- $cardData['deadline'] = zget($object, 'deadline', '');
- $cardData['severity'] = zget($object, 'severity', 0);
- $cardData['actionList'] = zget(zget($menus, $laneType, array()), $object->id, array());
- $cardData['canAssignTo'] = true;
- $cardData['title'] = zget($object, 'title', '');
- if($laneType == 'task')
- {
- $cardData['title'] = $object->name;
- $cardData['status'] = $object->status;
- $cardData['parent'] = $object->parent;
- $cardData['isParent'] = $object->isParent;
- $cardData['left'] = $object->left;
- $cardData['estStarted'] = $object->estStarted;
- $cardData['mode'] = $object->mode;
- $cardData['canAssignTo'] = common::hasPriv('task', 'assignTo') && common::hasDBPriv($object, 'task', 'assignto');
- if($object->mode == 'multi') $cardData['teamMembers'] = $object->teamMembers;
- }
- if($searchValue != '' && strpos($cardData['title'], $searchValue) === false) return array();
- return $cardData;
- }
- /**
- * 获取执行看板分组视图的数据。
- * Get kanban for group view.
- *
- * @param int $executionID
- * @param string $browseType
- * @param string $groupBy
- * @param string $searchValue
- * @param string $orderBy
- *
- * @access public
- * @return array
- */
- public function getKanban4Group($executionID, $browseType, $groupBy, $searchValue = '', $orderBy = 'id_asc')
- {
- /* Get card data. */
- $cardList = array();
- if(in_array($browseType, array('story', 'parentStory'))) $cardList = $this->loadModel('story')->getExecutionStories($executionID, 0, 't1.`order`_desc', 'allStory');
- if($browseType == 'epic') $cardList = $this->loadModel('story')->getExecutionStories($executionID, 0, 't1.`order`_desc', 'allStory', '0', 'epic');
- if($browseType == 'requirement') $cardList = $this->loadModel('story')->getExecutionStories($executionID, 0, 't1.`order`_desc', 'allStory', '0', 'requirement');
- if($browseType == 'bug') $cardList = $this->loadModel('bug')->getExecutionBugs($executionID);
- if($browseType == 'task') $cardList = $this->loadModel('execution')->getKanbanTasks($executionID);
- if($browseType == 'task' && $groupBy == 'assignedTo') $cardList = $this->appendTeamMember($cardList);
- /* Get objects cards menus. */
- $menus = array();
- if($browseType == 'story') $menus['story'] = $this->getKanbanCardMenu($executionID, $cardList, 'story');
- if($browseType == 'bug') $menus['bug'] = $this->getKanbanCardMenu($executionID, $cardList, 'bug');
- if($browseType == 'task') $menus['task'] = $this->getKanbanCardMenu($executionID, $cardList, 'task');
- if($groupBy == 'story' && $browseType == 'task' && !isset($this->lang->kanban->orderList[$orderBy])) $orderBy = 'id_asc';
- $lanes = $this->getLanes4Group($executionID, $browseType, $groupBy, $cardList, $orderBy);
- if(empty($lanes)) return array();
- $columns = $this->getCols4Group($executionID, $browseType);
- $cardGroup = array();
- foreach($columns as $column)
- {
- if(empty($column->cards)) continue;
- foreach($cardList as $card)
- {
- if(strpos($column->cards, ",$card->id,") !== false) $cardGroup[$column->columnType][$card->id] = $card;
- }
- }
- /* Build kanban group data. */
- $kanbanGroup = array();
- list($laneData, $columnData, $cardData) = $this->buildGroupKanban($lanes, $columns, $cardGroup, $searchValue, $groupBy, $browseType, $menus);
- $kanbanGroup['id'] = $executionID;
- $kanbanGroup['key'] = 'group' . $executionID;
- $kanbanGroup['data'] = array();
- $kanbanGroup['data']['lanes'] = $laneData;
- $kanbanGroup['data']['cols'] = array_values($columnData);
- $kanbanGroup['data']['items'] = $cardData;
- return array($kanbanGroup);
- }
- /**
- * 构建分组视图的看板泳道。
- * Build lanes for group kanban.
- *
- * @access public
- * @param int $executionID
- * @param string $browseType
- * @param string $groupBy
- * @param array $cardList
- * @param string $orderBy
- *
- * @return array
- */
- public function getLanes4Group($executionID, $browseType, $groupBy, $cardList, $orderBy = 'id_asc')
- {
- $lanes = array();
- $groupByList = array();
- foreach($cardList as $item)
- {
- if(!isset($groupByList[$item->$groupBy])) $groupByList[$item->$groupBy] = $item->$groupBy;
- if($groupBy == 'assignedTo' && !empty($item->teamMember))
- {
- foreach($item->teamMember as $account => $name)
- {
- if(!isset($groupByList[$account])) $groupByList[$account] = $account;
- }
- }
- }
- $objectPairs = $this->getObjectPairs($groupBy, $groupByList, $browseType, $orderBy);
- if($groupBy == 'story') $objects = $this->dao->select('*')->from(TABLE_STORY)->where('deleted')->eq(0)->andWhere('id')->in($groupByList)->orderBy($orderBy)->fetchAll('id');
- $laneColor = 0;
- $order = 1;
- foreach($objectPairs as $objectType => $objectName)
- {
- if(!isset($groupByList[$objectType]) && $objectType && !in_array($objectType, array('feature', 'design'))) continue;
- $lane = new stdclass();
- $lane->id = $groupBy . $objectType;
- $lane->type = $browseType;
- $lane->execution = $executionID;
- $lane->name = $objectName;
- $lane->order = (int)$order;
- $lane->color = $this->config->kanban->laneColorList[$laneColor];
- $lane->pri = (isset($objects) && isset($objects[$objectType]->pri)) ? $objects[$objectType]->pri : '';
- $lane->assignedTo = (isset($objects) && isset($objects[$objectType]->assignedTo)) ? $objects[$objectType]->assignedTo : '';
- $order += 1;
- $laneColor += 1;
- if($laneColor == count($this->config->kanban->laneColorList)) $laneColor = 0;
- $lanes[$objectType] = $lane;
- }
- return $lanes;
- }
- /**
- * 获取分组视图的看板列数据。
- * Get columns for group kanban.
- *
- * @param int $executionID
- * @param string $browseType
- * @access public
- * @return array
- */
- public function getCols4Group($executionID, $browseType)
- {
- $execution = $this->loadModel('execution')->getByID($executionID);
- $cards = $this->dao->select('t1.*, t1.cards, t2.`type` as columnType, t2.parent, t2.limit, t2.name as columnName, t2.color')->from(TABLE_KANBANCELL)->alias('t1')
- ->leftJoin(TABLE_KANBANCOLUMN)->alias('t2')->on('t1.`column` = t2.id')
- ->leftJoin(TABLE_KANBANLANE)->alias('t3')->on('t1.lane = t3.id')
- ->leftJoin(TABLE_KANBANREGION)->alias('t4')->on('t1.kanban = t4.kanban')
- ->where('t1.kanban')->eq($executionID)
- ->andWhere('t1.`type`')->eq($browseType)
- ->beginIF(isset($execution->type) && $execution->type == 'kanban')
- ->andWhere('t3.deleted')->eq(0)
- ->andWhere('t4.deleted')->eq(0)
- ->fi()
- ->orderBy('column_asc')
- ->fetchAll();
- $columns = array();
- foreach($cards as $card)
- {
- if(!isset($columns[$card->columnType])) $columns[$card->columnType] = $card;
- $columns[$card->columnType]->cards .= $card->cards;
- $columns[$card->columnType]->cards = ',' . trim($columns[$card->columnType]->cards, ',') . ',';
- }
- return $columns;
- }
- /**
- * 获取看板空间列表。
- * Get space list.
- *
- * @param string $browseType private|cooperation|public|involved
- * @param object $pager
- * @access public
- * @return array
- */
- public function getSpaceList($browseType, $pager = null)
- {
- $account = $this->app->user->account;
- $spaceIdList = $this->getCanViewObjects('kanbanspace', $browseType);
- $spaceList = $this->dao->select('*')->from(TABLE_KANBANSPACE)
- ->where('deleted')->eq(0)
- ->beginIF(in_array($browseType, array('private', 'cooperation', 'public')))->andWhere('type')->eq($browseType)->fi()
- ->beginIF($browseType == 'private' && !$this->app->user->admin)->andWhere('owner')->eq($account)->fi()
- ->beginIF($this->cookie->showClosed == 0)->andWhere('status')->ne('closed')->fi()
- ->andWhere('id')->in($spaceIdList)
- ->orderBy('id_desc')
- ->page($pager)
- ->fetchAll('id');
- $kanbanIdList = $this->getCanViewObjects('kanban', $browseType);
- $kanbanGroup = $this->getGroupBySpaceList(array_keys($spaceList), $kanbanIdList);
- foreach($spaceList as $spaceID => $space)
- {
- if(isset($kanbanGroup[$spaceID])) $space->kanbans = $kanbanGroup[$spaceID];
- }
- return $spaceList;
- }
- /**
- * 获取看板空间键值对。
- * Get space pairs.
- *
- * @param string $browseType private|cooperation|public|involved
- * @access public
- * @return array
- */
- public function getSpacePairs($browseType = 'private')
- {
- $spaceIdList = $this->getCanViewObjects('kanbanspace', $browseType);
- return $this->dao->select('id,name')->from(TABLE_KANBANSPACE)
- ->where('deleted')->eq(0)
- ->andWhere('id')->in($spaceIdList)
- ->beginIF(in_array($browseType, array('private', 'cooperation', 'public')))->andWhere('type')->eq($browseType)->fi()
- ->beginIF($this->cookie->showClosed == 0 && $browseType != 'showClosed')->andWhere('status')->ne('closed')->fi()
- ->orderBy('id_desc')
- ->fetchPairs('id');
- }
- /**
- * 获取看板键值对。
- * Get Kanban pairs.
- *
- * @access public
- * @return array
- */
- public function getKanbanPairs()
- {
- $kanbanIdList = $this->getCanViewObjects('kanban');
- return $this->dao->select('id,name')->from(TABLE_KANBAN)
- ->where('deleted')->eq(0)
- ->beginIF(!$this->app->user->admin)->andWhere('id')->in($kanbanIdList)->fi()
- ->orderBy('id_desc')
- ->fetchPairs('id');
- }
- /**
- * 获取有权查看的对象。
- * Get can view objects.
- *
- * @param string $objectType kanbanspace|kanban
- * @param string $param all|noclosed|private|cooperation|public|involved
- * @access public
- * @return array
- */
- public function getCanViewObjects($objectType = 'kanban', $param = 'all')
- {
- $table = $this->config->objectTables[$objectType];
- $objects = $this->dao->select('*')->from($table)
- ->where('deleted')->eq(0)
- ->beginIF(strpos($param, 'noclosed') !== false)->andWhere('status')->ne('closed')->fi()
- ->fetchAll('id', false);
- $spaceList = $objectType == 'kanban' ? $this->dao->select('id,owner,type')->from(TABLE_KANBANSPACE)->fetchAll('id') : array();
- if($param && $this->app->user->admin && strpos('private,involved', $param) === false) return array_keys($objects);
- $account = $this->app->user->account;
- foreach($objects as $objectID => $object)
- {
- if($objectType == 'kanbanspace' && $object->type == 'public' && $param != 'involved') continue;
- $remove = true;
- if($object->owner == $account) $remove = false;
- if(strpos(",{$object->team},", ",$account,") !== false) $remove = false;
- if(strpos(",{$object->whitelist},", ",$account,") !== false) $remove = false;
- if($objectType == 'kanban')
- {
- $spaceOwner = isset($spaceList[$object->space]->owner) ? $spaceList[$object->space]->owner : '';
- $spaceType = isset($spaceList[$object->space]->type) ? $spaceList[$object->space]->type : '';
- if(strpos(",$spaceOwner,", ",$account,") !== false) $remove = false;
- if($spaceType == 'public' && $param != 'involved') $remove = false;
- }
- if($remove) unset($objects[$objectID]);
- }
- return array_keys($objects);
- }
- /**
- * 创建看板空间。
- * Create a space.
- *
- * @param object $space
- * @access public
- * @return int|false
- */
- public function createSpace($space)
- {
- $account = $this->app->user->account;
- if($space->type == 'private') $space->owner = $account;
- if($space->type != 'private') $this->config->kanban->createspace->requiredFields .= ',owner';
- if(strpos(",{$space->team},", ",$account,") === false) $space->team .= ",$account";
- if(strpos(",{$space->team},", ",$space->owner,") === false) $space->team .= ",$space->owner";
- $this->dao->insert(TABLE_KANBANSPACE)->data($space)
- ->autoCheck()
- ->batchCheck($this->config->kanban->createspace->requiredFields, 'notempty')
- ->exec();
- if(dao::isError()) return false;
- $spaceID = $this->dao->lastInsertID();
- $this->dao->update(TABLE_KANBANSPACE)->set('`order`')->eq($spaceID)->where('id')->eq($spaceID)->exec();
- $this->loadModel('file')->saveUpload('kanbanspace', $spaceID);
- $this->file->updateObjectID($this->post->uid, $spaceID, 'kanbanspace');
- $this->loadModel('action')->create('kanbanSpace', $spaceID, 'created');
- return $spaceID;
- }
- /**
- * 编辑看板空间。
- * Update a space.
- *
- * @param object $space
- * @param int $spaceID
- * @access public
- * @return bool
- */
- public function updateSpace($space, $spaceID)
- {
- $oldSpace = $this->getSpaceById($spaceID);
- if($space->type == 'cooperation' || $space->type == 'public') $space->whitelist = '';
- if($space->type != 'private') $this->config->kanban->editspace->requiredFields .= ',owner';
- $this->dao->update(TABLE_KANBANSPACE)->data($space)
- ->autoCheck()
- ->batchCheck($this->config->kanban->editspace->requiredFields, 'notempty')
- ->where('id')->eq($spaceID)
- ->exec();
- if($oldSpace->type == 'private' && (in_array($space->type, array('cooperation', 'public'))))
- {
- $this->dao->update(TABLE_KANBAN)->set('team = whitelist')->set('whitelist')->eq('')->where('space')->eq($spaceID)->andWhere('deleted')->eq('0')->exec();
- }
- if(dao::isError()) return false;
- $this->loadModel('file')->saveUpload('kanbanspace', $spaceID);
- $this->file->updateObjectID($this->post->uid, $spaceID, 'kanbanspace');
- $changes = common::createChanges($oldSpace, $space);
- if($changes)
- {
- $actionID = $this->loadModel('action')->create('kanbanSpace', $spaceID, 'edited');
- $this->action->logHistory($actionID, $changes);
- }
- return true;
- }
- /**
- * 关闭看板空间。
- * Close a space.
- *
- * @param int $spaceID
- * @param object $space
- * @access public
- * @return bool
- */
- function closeSpace($spaceID, $space)
- {
- $oldSpace = $this->getSpaceById($spaceID);
- $this->dao->update(TABLE_KANBANSPACE)->data($space)
- ->autoCheck()
- ->where('id')->eq($spaceID)
- ->exec();
- if(dao::isError()) return false;
- $changes = common::createChanges($oldSpace, $space);
- if($changes)
- {
- $actionID = $this->loadModel('action')->create('kanbanSpace', $spaceID, 'closed', $this->post->comment);
- $this->action->logHistory($actionID, $changes);
- }
- return true;
- }
- /**
- * 激活看板空间。
- * Activate a space.
- *
- * @param int $spaceID
- * @param object $space
- * @access public
- * @return bool
- */
- function activateSpace($spaceID, $space)
- {
- $oldSpace = $this->getSpaceById($spaceID);
- $this->dao->update(TABLE_KANBANSPACE)->data($space)
- ->autoCheck()
- ->where('id')->eq($spaceID)
- ->exec();
- if(dao::isError()) return false;
- $changes = common::createChanges($oldSpace, $space);
- if($changes)
- {
- $actionID = $this->loadModel('action')->create('kanbanSpace', $spaceID, 'activated', $this->post->comment);
- $this->action->logHistory($actionID, $changes);
- }
- return true;
- }
- /**
- * 获取看板泳道键值对。
- * Get lane pairs by region id.
- *
- * @param int $regionID
- * @param string $type all|story|task|bug|common
- * @access public
- * @return array
- */
- public function getLanePairsByRegion($regionID, $type = 'all')
- {
- return $this->dao->select('id, name')->from(TABLE_KANBANLANE)
- ->where('deleted')->eq('0')
- ->andWhere('region')->in($regionID)
- ->beginIF($type != 'all')->andWhere('type')->eq($type)->fi()
- ->fetchPairs();
- }
- /**
- * 获取看板泳道分组。
- * Get lane by region id.
- *
- * @param array|int $regionID
- * @param string $type all|story|task|bug|common
- * @access public
- * @return array
- */
- public function getLaneGroupByRegion($regionID, $type = 'all')
- {
- return $this->dao->select('*')->from(TABLE_KANBANLANE)
- ->where('deleted')->eq('0')
- ->andWhere('region')->in($regionID)
- ->beginIF($type != 'all')->andWhere('type')->eq($type)->fi()
- ->fetchGroup('region');
- }
- /**
- * 创建看板泳道。
- * Create a lane.
- *
- * @param int $kanbanID
- * @param int $regionID
- * @param object $lane
- * @param string $mode new|copy
- * @access public
- * @return int|bool
- */
- public function createLane($kanbanID, $regionID, $lane = null, $mode = 'new')
- {
- if($mode == 'new')
- {
- $laneType = isset($_POST['laneType']) ? $_POST['laneType'] : 'common';
- if($laneType == 'common')
- {
- $sameNameLane = $this->dao->select('id')->from(TABLE_KANBANLANE)->where('region')->eq($regionID)->andWhere('name')->eq($lane->name)->andWhere('deleted')->eq('0')->limit(1)->fetch();
- if($sameNameLane)
- {
- dao::$errors['name'][] = $this->lang->kanbanlane->error->hasExist;
- return false;
- }
- }
- /* 共享看板列不能为空。 */
- if($lane->mode == 'sameAsOther' && $lane->otherLane == 0)
- {
- dao::$errors['otherLane'][] = $this->lang->kanbanlane->error->emptyOtherLane;
- return false;
- }
- $maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBANLANE)
- ->where('region')->eq($regionID)
- ->fetch('maxOrder');
- $lane->order = $maxOrder ? $maxOrder + 1 : 1;
- $lane->type = $laneType;
- $lane->execution = isset($_POST['laneType']) ? $kanbanID : 0;
- if($lane->mode == 'sameAsOther')
- {
- if($lane->otherLane) $lane->group = $this->dao->select('`group`')->from(TABLE_KANBANLANE)->where('id')->eq($lane->otherLane)->fetch('group');
- }
- elseif($lane->mode == 'independent')
- {
- $lane->group = $this->createGroup($kanbanID, $regionID);
- if($lane->type == 'common') $this->createDefaultColumns($regionID, $lane->group);
- }
- }
- $this->dao->insert(TABLE_KANBANLANE)->data($lane, $skip = 'mode,otherLane')
- ->batchCheck($this->config->kanban->require->createlane, 'notempty')
- ->autoCheck()
- ->exec();
- if(dao::isError()) return false;
- $laneID = $this->dao->lastInsertID();
- if($mode == 'new')
- {
- if($lane->type != 'common' && isset($lane->mode) && $lane->mode == 'independent') $this->createRDColumn($regionID, $lane->group, $laneID, $lane->type, $kanbanID);
- if($lane->mode == 'sameAsOther' || ($lane->type == 'common' && $lane->mode == 'independent'))
- {
- $columnIDList = $this->dao->select('id')->from(TABLE_KANBANCOLUMN)->where('deleted')->eq(0)->andWhere('archived')->eq(0)->andWhere('`group`')->eq($lane->group)->fetchPairs();
- foreach($columnIDList as $columnID)
- {
- $this->addKanbanCell($kanbanID, $laneID, $columnID, $lane->type);
- if(dao::isError()) return false;
- }
- }
- }
- $this->loadModel('action')->create('kanbanLane', $laneID, 'created');
- return $laneID;
- }
- /*
- * 创建看板。
- * Create a kanban.
- *
- * @param object $kanban
- * @access public
- * @return int|false
- */
- /**
- * @return int|false
- * @param object $kanban
- */
- public function create($kanban)
- {
- $account = $this->app->user->account;
- if($this->post->import == 'on') $kanban->object = implode(',', $this->post->importObjectList);
- if(strpos(",{$kanban->team},", ",$account,") === false) $kanban->team .= ",$account";
- if(strpos(",{$kanban->team},", ",$kanban->owner,") === false) $kanban->team .= ",$kanban->owner";
- if(!empty($kanban->space))
- {
- $maxOrder = $this->dao->select('MAX(`order`) AS maxOrder')->from(TABLE_KANBAN)
- ->where('space')->eq($kanban->space)
- ->fetch('maxOrder');
- $kanban->order = $maxOrder ? $maxOrder + 1 : 1;
- $space = $this->getSpaceById($kanban->space);
- if(!empty($space) && $space->type == 'private') $kanban->owner = $account;
- }
- $this->kanbanTao->createKanban($kanban);
- if(dao::isError()) return false;
- $kanbanID = $this->dao->lastInsertID();
- $kanbanInfo = $this->getByID($kanbanID);
- $this->loadModel('action')->create('kanban', $kanbanID, 'created');
- $this->loadModel('file')->saveUpload('kanban', $kanbanID);
- $this->file->updateObjectID($this->post->uid, $kanbanID, 'kanban');
- !empty($kanban->copyRegion) ? $this->copyRegions($kanbanInfo, $kanban->copyKanbanID): $this->createDefaultRegion($kanbanInfo);
- if(!empty($kanbanInfo->team) || !empty($kanbanInfo->whitelist))
- {
- $type = !empty($kanbanInfo->team) ? 'team' : 'whitelist';
- $kanbanMembers = empty($kanbanInfo->{$type}) ? array() : explode(',', $kanbanInfo->{$type});
- $this->addSpaceMembers($kanbanInfo->space, $type, $kanbanMembers);
- }
- return $kanbanID;
- }
- /**
- * 编辑看板。
- * Update a kanban.
- *
- * @param int $kanbanID
- * @param object $kanban
- * @access public
- * @return bool
- */
- public function update($kanbanID, $kanban)
- {
- $oldKanban = $this->getByID($kanbanID);
- if(strpos(",{$kanban->team},", ",$kanban->owner,") === false) $kanban->team .= ",$kanban->owner";
- $this->dao->update(TABLE_KANBAN)->data($kanban)
- ->autoCheck()
- ->batchCheck($this->config->kanban->edit->requiredFields, 'notempty')
- ->where('id')->eq($kanbanID)
- ->exec();
- if(dao::isError()) return false;
- $this->loadModel('file')->saveUpload('kanban', $kanbanID);
- $this->file->updateObjectID($this->post->uid, $kanbanID, 'kanban');
- if(!empty($kanban->team) || !empty($kanban->whitelist))
- {
- $type = !empty($kanban->team) ? 'team' : 'whitelist';
- $kanbanMembers = empty($kanban->{$type}) ? array() : explode(',', $kanban->{$type});
- $this->addSpaceMembers($kanban->space, $type, $kanbanMembers);
- }
- $changes = common::createChanges($oldKanban, $kanban);
- $actionID = $this->loadModel('action')->create('kanban', $kanbanID, 'edited');
- $this->action->logHistory($actionID, $changes);
- return true;
- }
- /**
- * 设置看板。
- * Setting kanban.
- *
- * @param int $kanbanID
- * @param object $kanban
- * @access public
- * @return bool
- */
- public function setting($kanbanID, $kanban)
- {
- $oldKanban = $this->getByID($kanbanID);
- if($this->post->import == 'off') $kanban->object = '';
- if($this->post->heightType == 'auto') $kanban->displayCards = 0;
- if(empty($kanban->displayCards)) $kanban->displayCards = 0;
- if($this->post->import == 'on') $kanban->object = $this->post->importObjectList ? implode(',', $this->post->importObjectList) : '';
- if(isset($_POST['heightType']) && $this->post->heightType == 'custom' && !$this->checkDisplayCards($kanban->displayCards)) return false;
- $this->dao->update(TABLE_KANBAN)->data($kanban)
- ->autoCheck()
- ->batchCheck($this->config->kanban->edit->requiredFields, 'notempty')
- ->checkIF(!$kanban->fluidBoard, 'colWidth', 'ge', $this->config->minColWidth)
- ->batchCheckIF($kanban->fluidBoard, 'minColWidth', 'ge', $this->config->minColWidth)
- ->checkIF($kanban->minColWidth >= $this->config->minColWidth && $kanban->fluidBoard, 'maxColWidth', 'gt', $kanban->minColWidth)
- ->where('id')->eq($kanbanID)
- ->exec();
- if(dao::isError()) return false;
- $changes = common::createChanges($oldKanban, $kanban);
- if($changes)
- {
- $actionID = $this->loadModel('action')->create('kanban', $kanbanID, 'edited');
- $this->action->logHistory($actionID, $changes);
- }
- return true;
- }
- /**
- * 激活看板。
- * Activate a kanban.
- *
- * @param int $kanbanID
- * @param object $kanban
- * @access public
- * @return bool
- */
- function activate($kanbanID, $kanban)
- {
- $oldKanban = $this->getByID($kanbanID);
- $this->dao->update(TABLE_KANBAN)->data($kanban)
- ->autoCheck()
- ->where('id')->eq($kanbanID)
- ->exec();
- if(dao::isError()) return false;
- $changes = common::createChanges($oldKanban, $kanban);
- $actionID = $this->loadModel('action')->create('kanban', $kanbanID, 'activated', $this->post->comment);
- $this->action->logHistory($actionID, $changes);
- return true;
- }
- /**
- * 关闭看板。
- * Close a kanban.
- *
- * @param int $kanbanID
- * @param object $kanban
- * @access public
- * @return bool
- */
- function close($kanbanID, $kanban)
- {
- $oldKanban = $this->getByID($kanbanID);
- $this->dao->update(TABLE_KANBAN)->data($kanban)
- ->autoCheck()
- ->where('id')->eq($kanbanID)
- ->exec();
- if(dao::isError()) return false;
- $changes = common::createChanges($oldKanban, $kanban);
- if($changes)
- {
- $actionID = $this->loadModel('action')->create('kanban', $kanbanID, 'closed', $this->post->comment);
- $this->action->logHistory($actionID, $changes);
- }
- return true;
- }
- /* 执行看板新增列和泳道。
- * Check whether kanban lane exists.
- *
- * @param object $execution
- * @access public
- * @return void
- */
- /**
- * @param object $execution
- */
- public function createLaneIfNotExist($execution)
- {
- $kanbanColumns = $this->dao->select('*')->from(TABLE_KANBANLANE)
- ->where('execution')->eq($execution->id)
- ->andWhere('deleted')->eq(0)
- ->fetchAll('type');
- $types = array_keys($kanbanColumns);
- /* 如果没有任意一种类型的 kanban, 生成一份新的 */
- if(empty($types))
- {
- $this->createExecutionLane($execution, 'all');
- }
- else
- {
- /* 如果缺少某种类型的 kanban,单独生成缺少的 */
- $kanbanTypes = $this->lang->kanban->type;
- unset($kanbanTypes['all']);
- unset($kanbanTypes['epic']);
- unset($kanbanTypes['requirement']);
- foreach($kanbanTypes as $type => $typeName)
- {
- if(!in_array($type, $types)) $this->createExecutionLane($execution, $type);
- }
- }
- }
- /**
- * 执行看板新增列和泳道。
- * Add execution Kanban lanes and columns.
- *
- * @param object $execution
- * @param string $type all|parentStory|story|bug|task|risk
- * @access public
- * @return void
- */
- public function createExecutionLane($execution, $type = 'all')
- {
- if(!$execution) return false;
- /* e.g $defaults = array('risk' => (object)array('name' => 'risk', 'color' => '#FF0000', 'order' => 20)); */
- $defaults = ($type != 'all') ? array($type => $this->config->kanban->default->$type) : (array)$this->config->kanban->default;
- $ERURLanes = array();
- $parentStoryLaneID = 0;
- foreach($defaults as $type => $lane)
- {
- /* 只有综合、需求、设计阶段,才可关联业需、用需。 */
- if($execution->type != 'stage' && !in_array($execution->attribute, array('mix', 'request', 'design')) && in_array($type, array('epic', 'requirement'))) continue;
- $lane->type = $type;
- $lane->execution = $execution->id;
- $lane->region = 0;
- $lane->group = 0;
- $lane->groupby = '';
- $lane->extra = '';
- $this->dao->insert(TABLE_KANBANLANE)->data($lane)->exec();
- $laneID = $this->dao->lastInsertId();
- if($type == 'parentStory') $parentStoryLaneID = $laneID;
- if(in_array($type, array('epic', 'requirement')))
- {
- $ERURLanes[$type] = $laneID;
- }
- else
- {
- if($type != 'risk') $this->createExecutionColumns($laneID, $type, $execution->id);
- else $this->createExecutionRiskColumns($laneID, $type, $execution->id);
- }
- }
- if($ERURLanes)
- {
- $columnIDList = $this->dao->select('`column`')->from(TABLE_KANBANCELL)->where('lane')->eq($parentStoryLaneID)->fetchPairs();
- foreach($ERURLanes as $type => $laneID)
- {
- foreach($columnIDList as $columnID)
- {
- $this->addKanbanCell($execution->id, $laneID, $columnID, $type);
- if(dao::isError()) return false;
- }
- }
- }
- return true;
- }
- /**
- * 创建执行看板列。
- * Create execution columns.
- *
- * @param int|array $laneID
- * @param string $type story|bug|task
- * @param int $executionID
- * @access public
- * @return void
- */
- public function createExecutionColumns($laneID, $type, $executionID)
- {
- $designColumnID = $devColumnID = $testColumnID = $resolvingColumnID = 0;
- $columns = array();
- if(in_array($type, array('epic', 'requirement', 'parentStory'))) $columns = $this->lang->kanban->ERURColumn;
- if($type == 'story') $columns = $this->lang->kanban->storyColumn;
- if($type == 'bug') $columns = $this->lang->kanban->bugColumn;
- if($type == 'task') $columns = $this->lang->kanban->taskColumn;
- if(empty($columns)) return;
- foreach($columns as $colType => $name)
- {
- $data = new stdclass();
- $data->name = $name;
- $data->color = '#333';
- $data->type = $colType;
- $data->region = 0;
- if(strpos(',designing,designed,', ",{$colType},") !== false) $data->parent = $designColumnID;
- if(strpos(',developing,developed,', ",{$colType},") !== false) $data->parent = $devColumnID;
- if(strpos(',testing,tested,', ",{$colType},") !== false) $data->parent = $testColumnID;
- if(strpos(',fixing,fixed,', ",{$colType},") !== false) $data->parent = $resolvingColumnID;
- if(strpos(',resolving,develop,test,', ",{$colType},") !== false) $data->parent = -1;
- $this->dao->insert(TABLE_KANBANCOLUMN)->data($data)->exec();
- $colID = $this->dao->lastInsertId();
- if($colType == 'design') $designColumnID = $colID;
- if($colType == 'develop') $devColumnID = $colID;
- if($colType == 'test') $testColumnID = $colID;
- if($colType == 'resolving') $resolvingColumnID = $colID;
- if(is_array($laneID))
- {
- foreach($laneID as $id) $this->addKanbanCell($executionID, $id, $colID, $type);
- }
- else
- {
- $this->addKanbanCell($executionID, $laneID, $colID, $type);
- }
- }
- }
- /**
- * 新增看板Cell。
- * Add kanban cell for new lane.
- *
- * @param int $kanbanID
- * @param int $laneID
- * @param int $colID
- * @param string $type story|task|bug|card
- * @param string $cardID
- * @access public
- * @return void
- */
- public function addKanbanCell($kanbanID, $laneID, $colID, $type, $cardID = '')
- {
- $cell = $this->dao->select('id, cards')->from(TABLE_KANBANCELL)
- ->where('kanban')->eq($kanbanID)
- ->andWhere('lane')->eq($laneID)
- ->andWhere('`column`')->eq($colID)
- ->andWhere('type')->eq($type)
- ->fetch();
- if(empty($cell))
- {
- $cell = new stdclass();
- $cell->kanban = $kanbanID;
- $cell->lane = $laneID;
- $cell->column = $colID;
- $cell->type = $type;
- $cell->cards = $cardID ? ",$cardID," : '';
- $this->dao->insert(TABLE_KANBANCELL)->data($cell)->exec();
- }
- else
- {
- $cell->cards = $cell->cards ? ",$cardID" . $cell->cards : ",$cardID,";
- $this->dao->update(TABLE_KANBANCELL)->set('cards')->eq($cell->cards)->where('id')->eq($cell->id)->exec();
- }
- }
- /**
- * 向看板空间添加成员。
- * Add space members.
- *
- * @param int $spaceID
- * @param string $type team|whitelist
- * @param array $kanbanMembers
- * @access public
- * @return void
- */
- public function addSpaceMembers($spaceID, $type, $kanbanMembers = array())
- {
- $space = $this->getSpaceById($spaceID);
- if(empty($space)) return;
- $spaceMembers = empty($space->{$type}) ? array() : explode(',', $space->{$type});
- $members = $space->{$type};
- $addMembers = array_diff($kanbanMembers, $spaceMembers);
- if(!empty($addMembers))
- {
- $addMembers = implode(',', $addMembers);
- $members .= ',' . trim($addMembers, ',');
- $this->dao->update(TABLE_KANBANSPACE)->set($type)->eq($members)->where('id')->eq($spaceID)->exec();
- }
- }
- /**
- * 从看板格子中移除卡片。
- * Remove kanban cell.
- *
- * @param string $type
- * @param int|array $removeCardID
- * @param array $kanbanList
- * @access public
- * @return void
- */
- public function removeKanbanCell($type, $removeCardID, $kanbanList)
- {
- $removeIDList = is_array($removeCardID) ? $removeCardID : array($removeCardID);
- foreach($removeIDList as $cardID)
- {
- if(empty($cardID)) continue;
- $this->dao->update(TABLE_KANBANCELL)->set("cards = REPLACE(cards, ',$cardID,', ',')")->where('type')->eq($type)->andWhere('kanban')->eq($kanbanList[$cardID])->exec();
- }
- $this->dao->update(TABLE_KANBANCELL)
- ->set('cards')->eq('')
- ->where('cards')->eq(',')
- ->andWhere('type')->eq($type)
- ->andWhere('kanban')->in($kanbanList)
- ->exec();
- }
- /**
- * 创建默认的专业研发看板。
- * Create a default RD kanban.
- *
- * @param object $execution
- * @access public
- * @return bool
- */
- public function createRDKanban($execution)
- {
- $regionID = $this->createRDRegion($execution);
- if(dao::isError()) return false;
- $this->createGroup((int)$execution->id, $regionID);
- if(dao::isError()) return false;
- $this->createRDLane((int)$execution->id, $regionID);
- if(dao::isError()) return false;
- return true;
- }
- /**
- * 创建默认的专业研发看板区域。
- * Create a default RD region.
- *
- * @param object $execution
- *
- * @access public
- * @return int|bool
- */
- public function createRDRegion($execution)
- {
- $region = new stdclass();
- $region->name = $this->lang->kanbanregion->default;
- $region->kanban = $execution->id;
- $region->createdBy = $this->app->user->account;
- $region->createdDate = helper::now();
- $region->order = 1;
- $this->dao->insert(TABLE_KANBANREGION)->data($region)
- ->check('name', 'unique', "kanban={$execution->id} AND deleted='0' AND space = '0'")
- ->autoCheck()
- ->exec();
- if(dao::isError()) return false;
- return $this->dao->lastInsertId();
- }
- /**
- * 创建默认的专业研发看板泳道。
- * Create default RD lanes.
- *
- * @param int $executionID
- * @param int $regionID
- *
- * @access public
- * @return bool
- */
- public function createRDLane($executionID, $regionID)
- {
- $execution = $this->loadModel('execution')->fetchByID($executionID);
- $project = $this->loadModel('project')->fetchByID($execution->project);
- $index = 0;
- foreach($this->lang->kanban->laneTypeList as $type => $name)
- {
- $groupID = $this->createGroup($executionID, $regionID);
- if(dao::isError()) return false;
- $lane = new stdclass();
- $lane->execution = $executionID;
- $lane->type = $type;
- $lane->region = $regionID;
- $lane->group = $groupID;
- $lane->name = $name;
- $lane->color = $this->config->kanban->laneColorList[$index];
- $lane->order = $this->config->kanban->RDLaneOrder[$type];
- $this->dao->insert(TABLE_KANBANLANE)->data($lane)->autoCheck()->exec();
- if(dao::isError()) return false;
- $laneID = $this->dao->lastInsertId();
- $this->createRDColumn($regionID, $groupID, $laneID, $type, $executionID);
- $index ++;
- }
- return true;
- }
- /**
- * 创建默认值的专业研发看板列。
- * Create default RD columns.
- *
- * @param int $regionID
- * @param int $groupID
- * @param int $laneID
- * @param string $laneType
- * @param int $executionID
- *
- * @access public
- * @return bool
- */
- public function createRDColumn($regionID, $groupID, $laneID, $laneType, $executionID)
- {
- $designColumnID = $devColumnID = $testColumnID = $resolvingColumnID = 0;
- if($laneType == 'parentStory') $columnList = $this->lang->kanban->ERURColumn;
- if($laneType == 'story') $columnList = $this->lang->kanban->storyColumn;
- if($laneType == 'bug') $columnList = $this->lang->kanban->bugColumn;
- if($laneType == 'task') $columnList = $this->lang->kanban->taskColumn;
- foreach($columnList as $type => $name)
- {
- $data = new stdclass();
- $data->name = $name;
- $data->color = '#333';
- $data->type = $type;
- $data->group = $groupID;
- $data->region = $regionID;
- if(strpos(',designing,designed,', $type) !== false) $data->parent = $designColumnID;
- if(strpos(',developing,developed,', $type) !== false) $data->parent = $devColumnID;
- if(strpos(',testing,tested,', $type) !== false) $data->parent = $testColumnID;
- if(strpos(',fixing,fixed,', $type) !== false) $data->parent = $resolvingColumnID;
- if(strpos(',design,develop,test,resolving,', $type) !== false) $data->parent = -1;
- $this->dao->insert(TABLE_KANBANCOLUMN)->data($data)->exec();
- if(dao::isError()) return false;
- $columnID = $this->dao->lastInsertId();
- if($type == 'design') $designColumnID = $columnID;
- if($type == 'develop') $devColumnID = $columnID;
- if($type == 'test') $testColumnID = $columnID;
- if($type == 'resolving') $resolvingColumnID = $columnID;
- $this->addKanbanCell($executionID, $laneID, $columnID, $laneType);
- }
- return true;
- }
- /**
- * 编辑区域。
- * Update a region.
- *
- * @param int $regionID
- * @access public
- * @return bool
- */
- public function updateRegion($regionID)
- {
- $region = new stdclass();
- $region->lastEditedBy = $this->app->user->account;
- $region->lastEditedDate = helper::now();
- $region->name = trim($this->post->name);
- $oldRegion = $this->getRegionById($regionID);
- $this->dao->update(TABLE_KANBANREGION)->data($region)
- ->autoCheck()
- ->batchcheck($this->config->kanban->editregion->requiredFields, 'notempty')
- ->where('id')->eq($regionID)
- ->exec();
- if(dao::isError()) return false;
- $changes = common::createChanges($oldRegion, $region);
- if($changes)
- {
- $actionID = $this->loadModel('action')->create('kanbanregion', $regionID, 'edited');
- $this->action->logHistory($actionID, $changes);
- }
- return true;
- }
- /**
- * 编辑看板泳道。
- * Update kanban lane.
- *
- * @param int $executionID
- * @param string $laneType
- * @param int $cardID
- * @access public
- * @return void
- */
- public function updateLane($executionID, $laneType = '', $cardID = 0)
- {
- $execution = $this->loadModel('execution')->getByID($executionID);
- if($execution->type == 'kanban')
- {
- $lanes = $this->dao->select('t2.*')->from(TABLE_KANBANREGION)->alias('t1')
- ->leftJoin(TABLE_KANBANLANE)->alias('t2')->on('t1.id=t2.region')
- ->leftJoin(TABLE_KANBANCELL)->alias('t3')->on('t2.id=t3.lane')
- ->where('t1.deleted')->eq(0)
- ->andWhere('t2.deleted')->eq(0)
- ->andWhere('t1.kanban')->eq($executionID)
- ->andWhere('t2.execution')->eq($executionID)
- ->beginIF($laneType)->andWhere('t2.type')->eq($laneType)->fi()
- ->beginIF(!empty($cardID))->andWhere('t3.cards')->like("%,$cardID,%")->fi()
- ->orderBy('t1.`order` asc, t2.`order` asc')
- ->fetchAll('id');
- if(count($lanes) > 1) $lanes = array_slice($lanes, 0, 2);
- }
- else
- {
- $lanes = $this->dao->select('*')->from(TABLE_KANBANLANE)
- ->where('execution')->eq($executionID)
- ->beginIF($laneType)->andWhere('type')->eq($laneType)->fi()
- ->fetchAll('id');
- }
- foreach($lanes as $lane) $this->refreshCards((array)$lane);
- }
- /**
- * 更新看板泳道上的卡片。
- * Refresh column cards.
- *
- * @param array $lane
- * @access public
- * @return void
- */
- public function refreshCards($lane)
- {
- if(common::isTutorialMode()) return;
- $laneID = zget($lane, 'id');
- $laneType = zget($lane, 'type');
- $executionID = zget($lane, 'execution');
- $otherCardList = '';
- $otherLanes = $this->dao->select('t2.id, t2.cards')->from(TABLE_KANBANLANE)->alias('t1')
- ->leftJoin(TABLE_KANBANCELL)->alias('t2')->on('t1.id=t2.lane')
- ->where('t1.id')->ne($laneID)
- ->andWhere('t1.execution')->eq($executionID)
- ->andWhere('t2.`type`')->eq($laneType)
- ->fetchPairs();
- foreach($otherLanes as $cardIDList)
- {
- $cardIDList = trim($cardIDList, ',');
- if(!empty($cardIDList)) $otherCardList .= ',' . $cardIDList;
- }
- $cardPairs = $this->dao->select('t2.type, t1.cards')->from(TABLE_KANBANCELL)->alias('t1')
- ->leftJoin(TABLE_KANBANCOLUMN)->alias('t2')->on('t1.`column` = t2.id')
- ->where('t1.kanban')->eq($executionID)
- ->andWhere('t1.lane')->eq($laneID)
- ->fetchPairs();
- if(empty($cardPairs)) return;
- $sourceCards = $cardPairs;
- if(in_array($laneType, array('epic', 'requirement', 'parentStory'))) $cardPairs = $this->kanbanTao->refreshERURCards($cardPairs, $executionID, $otherCardList, $laneType);
- if($laneType == 'story') $cardPairs = $this->kanbanTao->refreshStoryCards($cardPairs, $executionID, $otherCardList);
- if($laneType == 'bug') $cardPairs = $this->kanbanTao->refreshBugCards($cardPairs, $executionID, $otherCardList);
- if($laneType == 'task') $cardPairs = $this->kanbanTao->refreshTaskCards($cardPairs, $executionID, $otherCardList);
- if($laneType == 'risk' && in_array($this->config->edition, array('max', 'ipd'))) $cardPairs = $this->refreshRiskCards($cardPairs, $executionID, $otherCardList);
- $colPairs = $this->dao->select('t2.type, t2.id')->from(TABLE_KANBANCELL)->alias('t1')
- ->leftJoin(TABLE_KANBANCOLUMN)->alias('t2')->on('t1.`column` = t2.id')
- ->where('t1.kanban')->eq($executionID)
- ->andWhere('t1.lane')->eq($laneID)
- ->fetchPairs();
- $updated = false;
- foreach($cardPairs as $colType => $cards)
- {
- if(!isset($colPairs[$colType])) continue;
- if($sourceCards[$colType] == $cards) continue;
- $this->dao->update(TABLE_KANBANCELL)->set('cards')->eq($cards)->where('lane')->eq($laneID)->andWhere('`column`')->eq($colPairs[$colType])->exec();
- if(!$updated) $updated = true;
- }
- if($updated) $this->dao->update(TABLE_KANBANLANE)->set('lastEditedTime')->eq(helper::now())->where('id')->eq($laneID)->exec();
- }
- /**
- * 编辑看板列。
- * Update column.
- *
- * @param int $columnID
- * @param object $column
- * @access public
- * @return array|false
- */
- public function updateColumn($columnID, $column)
- {
- $oldColumn = $this->getColumnById($columnID);
- $this->dao->update(TABLE_KANBANCOLUMN)->data($column)
- ->autoCheck()
- ->batchcheck($this->config->kanban->setColumn->requiredFields, 'notempty')
- ->where('id')->eq($columnID)
- ->exec();
- if(dao::isError()) return false;
- $changes = common::createChanges($oldColumn, $column);
- return $changes;
- }
- /**
- * 激活卡片。
- * Activate a card.
- *
- * @param int $cardID
- * @access public
- * @return bool
- */
- public function activateCard($cardID)
- {
- if($this->post->progress >= 100 || $this->post->progress < 0)
- {
- dao::$errors['progress'][] = $this->lang->kanbancard->error->progressIllegal;
- return false;
- }
- $oldCard = $this->getCardByID($cardID);
- $this->dao->update(TABLE_KANBANCARD)->set('progress')->eq($this->post->progress ? $this->post->progress : 0)->set('status')->eq('doing')->where('id')->eq($cardID)->exec();
- $card = $this->getCardByID($cardID);
- $changes = common::createChanges($oldCard, $card);
- $actionID = $this->loadModel('action')->create('kanbanCard', $cardID, 'activated');
- $this->action->logHistory($actionID, $changes);
- return true;
- }
- /**
- * 编辑看板卡片。
- * Update a card.
- *
- * @param int $cardID
- * @param object $card
- * @access public
- * @return bool
- */
- public function updateCard($cardID, $card)
- {
- if($card->estimate < 0)
- {
- dao::$errors['estimate'] = $this->lang->kanbancard->error->recordMinus;
- return false;
- }
- if($card->end && ($card->begin > $card->end))
- {
- dao::$errors['end'] = $this->lang->kanbancard->error->endSmall;
- return false;
- }
- if($card->progress > 100 || $card->progress < 0)
- {
- dao::$errors['progress'] = $this->lang->kanbancard->error->progressIllegal;
- return false;
- }
- $oldCard = $this->getCardByID($cardID);
- if(!empty($this->post->assignedTo) && $oldCard->assignedTo != $this->post->assignedTo) $card->assignedDate = helper::now();
- $card->status = $card->progress == 100 ? 'done' : 'doing';
- $card = $this->loadModel('file')->processImgURL($card, $this->config->kanban->editor->editcard['id'], $this->post->uid);
- $this->dao->update(TABLE_KANBANCARD)->data($card)
- ->autoCheck()
- ->checkIF($card->estimate != '', 'estimate', 'float')
- ->batchcheck($this->config->kanban->editcard->requiredFields, 'notempty')
- ->where('id')->eq($cardID)
- ->exec();
- if(dao::isError()) return false;
- $this->file->saveUpload('kanbancard', $cardID);
- $this->file->updateObjectID($this->post->uid, $cardID, 'kanbancard');
- $changes = common::createChanges($oldCard, $card);
- if($changes)
- {
- $actionID = $this->loadModel('action')->create('kanbanCard', $cardID, 'edited');
- $this->action->logHistory($actionID, $changes);
- }
- return true;
- }
- /**
- * 设置看板在制品限制。
- * Set WIP limit.
- *
- * @param int $columnID
- * @param object $WIP
- * @access public
- * @return bool
- */
- public function setWIP($columnID, $WIP)
- {
- $oldColumn = $this->getColumnById($columnID);
- if(!preg_match("/^-?\d+$/", (string)$WIP->limit) || (!$WIP->noLimit && $WIP->limit <= 0))
- {
- dao::$errors['limit'] = $this->lang->kanban->error->mustBeInt;
- return false;
- }
- /* Check column limit. */
- $sumChildLimit = 0;
- if($oldColumn->parent == -1 && $WIP->limit != -1)
- {
- $childColumns = $this->dao->select('id,`limit`')->from(TABLE_KANBANCOLUMN)->where('parent')->eq($columnID)->andWhere('deleted')->eq(0)->fetchAll();
- foreach($childColumns as $childColumn) $sumChildLimit += $childColumn->limit;
- if($sumChildLimit > $WIP->limit)
- {
- dao::$errors['limit'] = $this->lang->kanban->error->parentLimitNote;
- }
- }
- elseif($oldColumn->parent > 0)
- {
- $parentColumn = $this->getColumnByID($oldColumn->parent);
- if($parentColumn->limit != -1)
- {
- $siblingLimit = $this->dao->select('`limit`')->from(TABLE_KANBANCOLUMN)
- ->where('`parent`')->eq($oldColumn->parent)
- ->andWhere('id')->ne($columnID)
- ->fetch('limit');
- $sumChildLimit = (int)$siblingLimit + (int)$WIP->limit;
- if($WIP->limit == -1 || $siblingLimit == -1 || $sumChildLimit > $parentColumn->limit)
- {
- dao::$errors['limit'] = $this->lang->kanban->error->childLimitNote;
- }
- }
- }
- if(dao::isError()) return false;
- $this->dao->update(TABLE_KANBANCOLUMN)->data($WIP, 'noLimit')
- ->autoCheck()
- ->checkIF($WIP->limit != -1, 'limit', 'gt', 0)
- ->where('id')->eq($columnID)
- ->exec();
- return !dao::isError();
- }
- /**
- * 设置看板列。
- * Set lane info.
- *
- * @param int $laneID
- * @param object $lane
- * @access public
- * @return bool
- */
- public function setLane($laneID, $lane)
- {
- $this->dao->update(TABLE_KANBANLANE)->data($lane)
- ->autoCheck()
- ->batchcheck($this->config->kanban->setlane->requiredFields, 'notempty')
- ->where('id')->eq($laneID)
- ->exec();
- return !dao::isError();
- }
- /**
- * 获取看板右上角的工具栏。
- * Get kanban pageToolBar.
- *
- * @param object $kanban
- * @access public
- * @return string
- */
- public function getPageToolBar($kanban)
- {
- $actions = '';
- $actions .= "<a href='javascript:$(\"#kanbanList\").fullscreen();' id='fullScreenBtn' class='toolbar-item ghost btn btn-default'><i class='icon icon-fullscreen'></i> {$this->lang->kanban->fullScreen}</a>";
- $printKanbanBtn = (common::hasPriv('kanban', 'edit') || ($kanban->status == 'active' && common::hasPriv('kanban', 'close')) || common::hasPriv('kanban', 'delete') || ($kanban->status == 'closed' && common::hasPriv('kanban', 'activate')));
- if($printKanbanBtn)
- {
- $actions .= "<a class='toolbar-item ghost btn btn-default setting' type='button' data-toggle='dropdown' data-target='#kanbanActionMenu'>" . '<i class="icon icon-edit"></i> ' . $this->lang->edit . '</a>';
- $actions .= "<menu id='kanbanActionMenu' class='dropdown-menu text-left'>";
- $columnActions = '';
- $actions .= $columnActions;
- $commonActions = '';
- if($columnActions && $commonActions)
- {
- $actions .= "<li class='divider'></li>";
- }
- $actions .= $commonActions;
- $kanbanActions = '';
- if(common::hasPriv('kanban', 'edit')) $kanbanActions .= '<li class="menu-item item">' . html::a(helper::createLink('kanban', 'edit', "kanbanID=$kanban->id", '', true), '<i class="icon icon-edit"></i>' . $this->lang->kanban->edit, '', "class='listitem item-inner menu-item-inner state' data-toggle='modal'") . '</li>';
- if(common::hasPriv('kanban', 'close') && $kanban->status == 'active') $kanbanActions .= '<li class="menu-item item">' . html::a(helper::createLink('kanban', 'close', "kanbanID=$kanban->id", '', true), '<i class="icon icon-off"></i>' . $this->lang->kanban->close, '', "class='listitem item-inner menu-item-inner state' data-toggle='modal'") . '</li>';
- if(common::hasPriv('kanban', 'activate') && $kanban->status == 'closed') $kanbanActions .= '<li class="menu-item item">' . html::a(helper::createLink('kanban', 'activate', "kanbanID=$kanban->id", '', true), '<i class="icon icon-magic"></i>' . $this->lang->kanban->activate, '', "class='listitem item-inner menu-item-inner state' data-toggle='modal'") . '</li>';
- if(common::hasPriv('kanban', 'delete')) $kanbanActions .= '<li class="menu-item item">' . html::a(helper::createLink('kanban', 'delete', "kanbanID=$kanban->id"), '<i class="icon icon-trash"></i>' . $this->lang->kanban->delete, 'hiddenwin', "class='listitem item-inner menu-item-inner state ajax-submit' data-confirm={$this->lang->kanban->confirmDeleteKanban}") . '</li>';
- if($commonActions && $kanbanActions)
- {
- $actions .= "<li class='divider'></li>";
- }
- $actions .= $kanbanActions;
- $actions .= "</menu>";
- }
- if(common::hasPriv('kanban', 'setting'))
- {
- $actions .= html::a(helper::createLink('kanban', 'setting', "kanbanID=$kanban->id", '', true), '<i class="icon icon-cog-outline"></i> ' . $this->lang->kanban->setting, '', "class='toolbar-item ghost btn btn-default' data-toggle='modal'");
- }
- return $actions;
- }
- /**
- * 排序看板分组。
- * Sort kanban group.
- *
- * @param int $region
- * @param array $groups
- * @access public
- * @return bool
- */
- public function sortGroup($region, $groups)
- {
- $groupList = $this->getGroupList($region);
- $order = 1;
- foreach($groups as $groupID)
- {
- if(!$groupID) continue;
- if(!isset($groupList[$groupID])) continue;
- $this->dao->update(TABLE_KANBANGROUP)->set('`order`')->eq($order)->where('id')->eq($groupID)->exec();
- $order++;
- }
- return !dao::isError();
- }
- /**
- * 移动看板卡片。
- * Move a card.
- *
- * @param int $cardID
- * @param int $fromColID
- * @param int $toColID
- * @param int $fromLaneID
- * @param int $toLaneID
- * @param int $kanbanID
- * @access public
- * @return void
- */
- public function moveCard($cardID, $fromColID, $toColID, $fromLaneID, $toLaneID, $kanbanID = 0)
- {
- $groupBy = ($this->session->execGroupBy && ($this->app->tab == 'execution' || $this->config->vision == 'lite')) ? $this->session->execGroupBy : '';
- $fromCell = $this->dao->select('id,cards,lane')->from(TABLE_KANBANCELL)
- ->where('`column`')->eq($fromColID)
- ->beginIF((!$groupBy || $groupBy == 'default') && $fromLaneID)->andWhere('lane')->eq($fromLaneID)->fi()
- ->beginIF($groupBy && $groupBy != 'default')
- ->andWhere('type')->eq($this->session->execLaneType)
- ->andWhere('cards')->like("%,$cardID,%")
- ->fi()
- ->fetch();
- if($groupBy && $groupBy != 'default') $fromLaneID = $toLaneID = $fromCell->lane;
- if(empty($fromLaneID) && $fromCell) $fromLaneID = $toLaneID = $fromCell->lane;
- $fromCells[$fromCell->id] = $fromCell;
- /* Remove all cells with cardID in fromCell. */
- $fromLane = $this->getLaneById($fromLaneID);
- $fromCells += $this->dao->select('t1.id as id,t1.cards,t1.lane')->from(TABLE_KANBANCELL)->alias('t1')
- ->leftJoin(TABLE_KANBANLANE)->alias('t2')->on('t1.lane=t2.id')
- ->where('t1.type')->eq($fromLane->type)
- ->andWhere('t1.id')->ne($fromCell->id)
- ->andWhere('t2.region')->eq($fromLane->region)
- ->andWhere('t1.cards')->like("%,$cardID,%")
- ->fetchAll('id');
- foreach($fromCells as $fromCell)
- {
- $fromCellCards = explode(',', $fromCell->cards);
- $fromCellCards = array_unique($fromCellCards);
- $fromCellCards = array_filter($fromCellCards);
- $fromCellCards = implode(',', $fromCellCards);
- $fromCardList = str_replace(",$cardID,", ',', ",$fromCellCards,");
- if($fromCardList == ',') $fromCardList = '';
- $this->dao->update(TABLE_KANBANCELL)->set('cards')->eq($fromCardList)->where('id')->eq($fromCell->id)->exec();
- }
- /* Add cardID to toCell. */
- $toCell = $this->dao->select('*')->from(TABLE_KANBANCELL)->where('lane')->eq($toLaneID)->andWhere('`column`')->eq($toColID)->fetch();
- $toCellCards = $this->dao->select('cards')->from(TABLE_KANBANCELL)->where('lane')->eq($toLaneID)->andWhere('`column`')->eq($toColID)->fetch('cards');
- $kanbanID = $kanbanID == 0 ? $toCell->kanban : $kanbanID;
- if(!$toCell) $this->addKanbanCell($kanbanID, $toLaneID, $toColID, 'common');
- $toCardList = rtrim($toCellCards, ',') . ",$cardID,";
- $this->dao->update(TABLE_KANBANCELL)->set('cards')->eq($toCardList)->where('`column`')->eq($toColID)->andWhere('lane')->eq($toLaneID)->exec();
- $toLane = $this->getLaneById($toLaneID);
- $this->dao->update(TABLE_KANBANCARD)->set('group')->eq($toLane->group)->where('id')->eq($cardID)->exec();
- }
- /**
- * 更新卡片的颜色。
- * Update a card's color.
- *
- * @param int $cardID
- * @param string $color
- * @access public
- * @return void
- */
- public function updateCardColor($cardID, $color)
- {
- $this->dao->update(TABLE_KANBANCARD)->set('`color`')->eq('#' . $color)->where('id')->eq($cardID)->exec();
- }
- /**
- * 归档看板列。
- * Archive a column.
- *
- * @param int $columnID
- * @access public
- * @return void
- */
- public function archiveColumn($columnID)
- {
- $this->dao->update(TABLE_KANBANCOLUMN)
- ->set('archived')->eq(1)
- ->where('id')->eq($columnID)
- ->exec();
- if(!dao::isError()) $this->loadModel('action')->create('kanbancolumn', $columnID, 'archived');
- }
- /**
- * 还原看板列。
- * Restore a column.
- *
- * @param int $columnID
- * @access public
- * @return void
- */
- public function restoreColumn($columnID)
- {
- $column = $this->getColumnByID($columnID);
- if($column->parent)
- {
- $parent = $this->getColumnByID($column->parent);
- /* If the parent column is normal now, put its card into child column. */
- if($parent && $parent->parent != -1)
- {
- $parentCells = $this->dao->select('*')->from(TABLE_KANBANCELL)
- ->where('`column`')->eq($column->parent)
- ->andWhere('type')->eq('common')
- ->fetchAll('id');
- foreach($parentCells as $cell)
- {
- $cards = $this->dao->select('cards')->from(TABLE_KANBANCELL)
- ->where('lane')->eq($cell->lane)
- ->andWhere('`column`')->eq($columnID)
- ->andWhere('type')->eq('common')
- ->fetch('cards');
- $cards = $cards ? $cards . ltrim($cell->cards, ',') : $cell->cards;
- $this->dao->update(TABLE_KANBANCELL)->set('cards')->eq($cards)
- ->where('lane')->eq($cell->lane)
- ->andWhere('`column`')->eq($columnID)
- ->andWhere('type')->eq('common')
- ->exec();
- $this->dao->update(TABLE_KANBANCELL)->set('cards')->eq('')
- ->where('lane')->eq($cell->lane)
- ->andWhere('`column`')->eq($cell->column)
- ->andWhere('type')->eq('common')
- ->exec();
- }
- $this->dao->update(TABLE_KANBANCOLUMN)->set('parent')->eq(-1)->where('id')->eq($column->parent)->exec();
- }
- }
- $this->dao->update(TABLE_KANBANCOLUMN)
- ->set('archived')->eq(0)
- ->where('id')->eq($columnID)
- ->exec();
- if(!dao::isError()) $this->loadModel('action')->create('kanbancolumn', $columnID, 'restore');
- }
- /**
- * 归档看板卡片。
- * Archive a card.
- *
- * @param int $cardID
- * @access public
- * @return bool
- */
- public function archiveCard($cardID)
- {
- $oldCard = $this->getCardByID($cardID);
- $this->dao->update(TABLE_KANBANCARD)
- ->set('archived')->eq(1)
- ->set('archivedBy')->eq($this->app->user->account)
- ->set('archivedDate')->eq(helper::now())
- ->where('id')->eq($cardID)
- ->exec();
- $card = $this->getCardByID($cardID);
- if(dao::isError()) return false;
- $changes = common::createChanges($oldCard, $card);
- if($changes)
- {
- $actionID = $this->loadModel('action')->create('kanbancard', $cardID, 'archived');
- $this->action->logHistory($actionID, $changes);
- }
- return true;
- }
- /**
- * 还原看板卡片。
- * Restore a card.
- *
- * @param int $cardID
- * @access public
- * @return bool
- */
- public function restoreCard($cardID)
- {
- $oldCard = $this->getCardByID($cardID);
- $this->dao->update(TABLE_KANBANCARD)
- ->set('archived')->eq(0)
- ->set('archivedBy')->eq('')
- ->set('archivedDate')->eq(null)
- ->where('id')->eq($cardID)
- ->exec();
- if(dao::isError()) return false;
- $card = $this->getCardByID($cardID);
- $changes = common::createChanges($oldCard, $card);
- if($changes)
- {
- $actionID = $this->loadModel('action')->create('kanbancard', $cardID, 'restore');
- $this->action->logHistory($actionID, $changes);
- }
- return true;
- }
- /**
- * 删除子列时,将子列的卡片移动到第一个子列,或者移动到父列。
- * Process cards when delete a column.
- *
- * @param object $column
- * @access public
- * @return void
- */
- public function processCards($column)
- {
- $firstColumnID = $this->dao->select('id')->from(TABLE_KANBANCOLUMN)
- ->where('parent')->eq($column->parent)
- ->andWhere('id')->ne($column->id)
- ->andWhere('deleted')->eq('0')
- ->andWhere('archived')->eq('0')
- ->orderBy('`order` asc')
- ->fetch('id');
- $extendID = $firstColumnID;
- if(!$firstColumnID)
- {
- $extendID = $column->parent;
- $this->dao->update(TABLE_KANBANCOLUMN)->set('parent')->eq(0)->where('id')->eq($column->parent)->exec();
- }
- $cells = $this->dao->select('*')->from(TABLE_KANBANCELL)
- ->where('`column`')->eq($column->id)
- ->andWhere('type')->eq('common')
- ->fetchAll('id', false);
- foreach($cells as $cell)
- {
- $extendCards = $this->dao->select('cards')->from(TABLE_KANBANCELL)
- ->where('lane')->eq($cell->lane)
- ->andWhere('`column`')->eq($extendID)
- ->andWhere('type')->eq('common')
- ->fetch('cards');
- $extendCards = $extendCards ? $extendCards . ltrim($cell->cards, ',') : $cell->cards;
- $this->dao->update(TABLE_KANBANCELL)->set('cards')->eq($extendCards)
- ->where('lane')->eq($cell->lane)
- ->andWhere('`column`')->eq($extendID)
- ->andWhere('type')->eq('common')
- ->exec();
- }
- }
- /**
- * 获取看板空间。
- * Get space by id.
- *
- * @param int $spaceID
- * @access public
- * @return object
- */
- public function getSpaceById($spaceID)
- {
- $space = $this->dao->findById($spaceID)->from(TABLE_KANBANSPACE)->fetch();
- if($space) $space = $this->loadModel('file')->replaceImgURL($space, 'desc');
- return $space;
- }
- /**
- * Get kanban group by space id list.
- *
- * @param array $spaceIdList
- * @param array $kanbanIdList
- * @access public
- * @return array
- */
- public function getGroupBySpaceList($spaceIdList, $kanbanIdList = array())
- {
- $spaceList = $this->dao->select('*')->from(TABLE_KANBAN)
- ->where('deleted')->eq(0)
- ->andWhere('space')->in($spaceIdList)
- ->beginIF($kanbanIdList)->andWhere('id')->in($kanbanIdList)->fi()
- ->fetchGroup('space', 'id');
- $kanbanIDList = array();
- foreach($spaceList as $kanbanList) $kanbanIDList = array_merge_recursive($kanbanIDList, array_keys($kanbanList));
- $cardsCount = $this->dao->select('kanban, COUNT(1) AS count')->from(TABLE_KANBANCARD)
- ->where('deleted')->eq(0)
- ->andWhere('kanban')->in($kanbanIDList)
- ->groupBy('kanban')
- ->fetchPairs('kanban');
- foreach($spaceList as $kanbanList)
- {
- foreach($kanbanList as $kanban)
- {
- $kanban->cardsCount = zget($cardsCount, $kanban->id, 0);
- }
- }
- return $spaceList;
- }
- /**
- * 获取看板组。
- * Get group list by region.
- *
- * @param int $regionID
- * @access public
- * @return array
- */
- public function getGroupList($regionID)
- {
- return $this->dao->select('*')->from(TABLE_KANBANGROUP)
- ->where('region')->eq($regionID)
- ->orderBy('order')
- ->fetchAll('id');
- }
- /**
- * 获取看板列。
- * Get column by id.
- *
- * @param int $columnID
- * @access public
- * @return object
- */
- public function getColumnByID($columnID)
- {
- if(common::isTutorialMode()) return $this->loadModel('tutorial')->getColumn();
- $column = $this->dao->select('t1.*, t2.type as laneType')->from(TABLE_KANBANCOLUMN)->alias('t1')
- ->leftJoin(TABLE_KANBANCELL)->alias('t2')->on('t1.id=t2.column')
- ->where('t1.id')->eq($columnID)
- ->fetch();
- if(empty($column)) return false;
- if($column->parent > 0) $column->parentName = $this->dao->findById($column->parent)->from(TABLE_KANBANCOLUMN)->fetch('name');
- return $column;
- }
- /**
- * 根据字段获取看板列。
- * Get columns by field.
- *
- * @param string $field parent|region|group
- * @param int $fieldID
- * @param string $archived
- * @param string $deleted
- * @access public
- * @return array
- */
- public function getColumnsByField($field = '', $fieldID = 0, $archived = '0', $deleted = '0')
- {
- return $this->dao->select('*')->from(TABLE_KANBANCOLUMN)
- ->where('1 = 1')
- ->beginIF($field)->andWhere($field)->eq($fieldID)->fi()
- ->beginIF($archived != '')->andWhere('archived')->eq($archived)->fi()
- ->beginIF($deleted != '')->andWhere('deleted')->eq($deleted)->fi()
- ->orderBy('order')
- ->fetchAll('id');
- }
- /**
- * 获取泳道的看板列ID。
- * Get column ID by lane ID.
- *
- * @param int $laneID
- * @param string $columnType
- * @access public
- * @return string|int
- */
- public function getColumnIDByLaneID($laneID, $columnType)
- {
- return $this->dao->select('t1.column')->from(TABLE_KANBANCELL)->alias('t1')
- ->leftJoin(TABLE_KANBANCOLUMN)->alias('t2')->on('t1.column = t2.id')
- ->where('t1.lane')->eq($laneID)
- ->andWhere('t2.type')->eq($columnType)
- ->fetch('column');
- }
- /**
- * 根据ID获取看板泳道。
- * Get lane by id.
- *
- * @param int $laneID
- * @access public
- * @return object
- */
- public function getLaneById($laneID)
- {
- return $this->dao->findById($laneID)->from(TABLE_KANBANLANE)->fetch();
- }
- /**
- * 获取看板卡片。
- * Get card by id.
- *
- * @param int $cardID
- * @access public
- * @return object|false
- */
- public function getCardByID($cardID)
- {
- $card = $this->dao->select('*')->from(TABLE_KANBANCARD)->where('id')->eq($cardID)->fetch();
- if(!$card) return false;
- $card = $this->loadModel('file')->replaceImgURL($card, 'desc');
- return $card;
- }
- /**
- * 获取某个对象的看板卡片。
- * Get cards by object id.
- *
- * @param string $objectType kanban|region|group|lane|column
- * @param int|string $objectID
- * @param string $archived
- * @param string $deleted
- * @access public
- * @return array
- */
- public function getCardsByObject($objectType = '', $objectID = 0, $archived = '0', $deleted = '0')
- {
- return $this->dao->select('*')->from(TABLE_KANBANCARD)
- ->where('1 = 1')
- ->beginIF($objectType)->andWhere($objectType)->eq($objectID)->fi()
- ->beginIF($archived != '')->andWhere('archived')->eq($archived)->fi()
- ->beginIF($deleted != '')->andWhere('deleted')->eq($deleted)->fi()
- ->orderBy('order')
- ->fetchAll('id', false);
- }
- /**
- * 保存看板卡片到期提醒。
- * Save kanban card due reminders.
- *
- * @param array $cards
- * @access public
- * @return void
- */
- public function saveCardNotice($cards)
- {
- if(empty($cards)) $cards = $this->loadModel('report')->getUserKanbanCards();
- if(empty($cards)) return;
- $today = helper::today();
- foreach($cards as $user => $cardList)
- {
- foreach($cardList as $card)
- {
- $url = helper::createLink('kanban', 'view', "kanbanID={$card->kanban}") . "#app=kanban";
- $data = $today > $card->deadline ? $this->lang->kanban->due : $this->lang->kanban->nearing;
- $data .= html::a($url, "#{$card->id} {$card->name}");
- $notify = new stdclass();
- $notify->objectType = 'message';
- $notify->action = 0;
- $notify->toList = ",{$user},";
- $notify->data = $data;
- $notify->status = 'wait';
- $notify->createdBy = 'admin';
- $notify->createdDate = helper::now();
- $notify->sendTime = null;
- $this->dao->insert(TABLE_NOTIFY)->data($notify)->exec();
- }
- }
- }
- /**
- * 获取可转入的看板卡片。
- * Get cards to import.
- *
- * @param int $kanbanID
- * @param int $excludedID
- * @param object $pager
- * @access public
- * @return array
- */
- public function getCards2Import($kanbanID = 0, $excludedID = 0, $pager = null)
- {
- $kanbanIdList = $this->getCanViewObjects();
- return $this->dao->select('*')->from(TABLE_KANBANCARD)
- ->where('deleted')->eq(0)
- ->andWhere('archived')->eq(0)
- ->andWhere('fromID')->eq(0)
- ->andWhere('kanban')->in($kanbanIdList)
- ->beginIF($kanbanID)->andWhere('kanban')->eq($kanbanID)->fi()
- ->beginIF($excludedID)->andWhere('kanban')->ne($excludedID)->fi()
- ->orderBy('order')
- ->page($pager)
- ->fetchAll('id', false);
- }
- /**
- * 获取看板卡片的操作按钮。
- * Get Kanban cards menus by execution id.
- *
- * @param int $executionID
- * @param array $objects
- * @param string $objecType story|bug|task
- * @access public
- * @return array
- */
- public function getKanbanCardMenu($executionID, $objects, $objecType)
- {
- $this->app->loadLang('execution');
- $execution = $this->loadModel('execution')->getByID($executionID);
- if(!common::canModify('execution', $execution)) return array();
- $menus = array();
- switch ($objecType)
- {
- case 'epic':
- $menus = $this->kanbanTao->getERURCardMenu($executionID, $objects);
- break;
- case 'requirement':
- $menus = $this->kanbanTao->getERURCardMenu($executionID, $objects);
- break;
- case 'parentStory':
- $menus = $this->kanbanTao->getERURCardMenu($executionID, $objects);
- break;
- case 'story':
- if($execution) $menus = $this->kanbanTao->getStoryCardMenu($execution, $objects);
- break;
- case 'bug':
- $menus = $this->kanbanTao->getBugCardMenu($objects);
- break;
- case 'task':
- if(!isset($_SESSION['limitedExecutions'])) $this->loadModel('execution')->getLimitedExecution();
- $menus = $this->kanbanTao->getTaskCardMenu($objects, $executionID);
- break;
- case 'risk':
- $menus = $this->kanbanTao->getRiskCardMenu($objects);
- break;
- }
- return $menus;
- }
- /**
- * 获取卡片的发送人和抄送人。
- * Get toList and ccList.
- *
- * @param object $card
- * @access public
- * @return bool|array
- */
- public function getToAndCcList($card)
- {
- /* Set toList and ccList. */
- $toList = $card->createdBy;
- $ccList = trim($card->assignedTo, ',');
- if(empty($toList))
- {
- if(empty($ccList)) return false;
- if(strpos($ccList, ',') === false)
- {
- $toList = $ccList;
- $ccList = '';
- }
- else
- {
- $commaPos = strpos($ccList, ',');
- $toList = substr($ccList, 0, $commaPos);
- $ccList = substr($ccList, $commaPos + 1);
- }
- }
- return array($toList, $ccList);
- }
- /**
- * 检查操作按钮是否可点。
- * Check if user can execute an action.
- *
- * @param object $object
- * @param string $action
- * @access public
- * @return bool
- */
- public function isClickable($object, $action)
- {
- $action = strtolower($action);
- $clickable = commonModel::hasPriv('kanban', $action);
- if(!$clickable) return false;
- switch($action)
- {
- case 'sortlane' :
- case 'deletelane' :
- if($object->deleted != '0') return false;
- $count = $this->dao->select('COUNT(id) AS count')->from(TABLE_KANBANLANE)
- ->where('deleted')->eq('0')
- ->andWhere('region')->eq($object->region)
- ->beginIF($action == 'sortlane')->andWhere('`group`')->eq($object->group)->fi()
- ->fetch('count');
- return $count > 1;
- case 'splitcolumn' :
- return $object->parent == 0;
- case 'restorecolumn' :
- if($object->parent > 0)
- {
- $parent = $this->getColumnByID($object->parent);
- if(!empty($parent) && ($parent->deleted == '1' || $parent->archived == '1')) return false;
- }
- return $object->archived == '1';
- case 'archivecolumn' :
- return $object->archived == '0'; // The column has been archived.
- case 'deletecolumn' :
- return $object->deleted == '0';
- case 'sortcolumn':
- return $object->deleted == '0';
- case 'editcard':
- return $object->deleted == '0' && $object->archived == '0';
- case 'finishcard':
- return $object->status != 'done' && $object->deleted == '0' && $object->archived == '0';
- case 'activatecard':
- return $object->status == 'done' && $object->deleted == '0' && $object->archived == '0';
- case 'deletecard':
- return $object->deleted == '0';
- case 'archivecard':
- return $object->archived == '0';
- case 'restorecard':
- return $object->archived == '1';
- }
- return true;
- }
- /**
- * 获取泳道的数量。
- * Get kanban lane count.
- *
- * @param int $kanbanID
- * @param string $type
- * @access public
- * @return int
- */
- public function getLaneCount($kanbanID, $type = 'common')
- {
- if($type == 'common' || $type == 'kanban')
- {
- return $this->dao->select('COUNT(t2.id) as count')->from(TABLE_KANBANREGION)->alias('t1')
- ->leftJoin(TABLE_KANBANLANE)->alias('t2')->on('t1.id=t2.region')
- ->where('t1.kanban')->eq($kanbanID)
- ->andWhere('t1.deleted')->eq(0)
- ->andWhere('t2.deleted')->eq(0)
- ->beginIF($type == 'common')->andWhere('t2.type')->eq('common')->fi()
- ->beginIF($type != 'common')->andWhere('t2.type')->ne('common')->fi()
- ->fetch('count');
- }
- else
- {
- return $this->dao->select('COUNT(id) as count')->from(TABLE_KANBANLANE)
- ->where('execution')->eq($kanbanID)
- ->andWhere('deleted')->eq(0)
- ->fetch('count');
- }
- }
- /**
- * 检查卡片数量是否合法。
- * Check display card count.
- *
- * @param int $count
- * @access public
- * @return bool
- */
- public function checkDisplayCards($count)
- {
- if(!preg_match("/^-?\d+$/", (string)$count) || $count <= DEFAULT_CARDCOUNT || $count > MAX_CARDCOUNT) dao::$errors['displayCards'] = $this->lang->kanbanlane->error->mustBeInt;
- return !dao::isError();
- }
- /**
- * 获取看板动态刷新的回调函数数组。
- * Get kanban callback data.
- *
- * @param int $kanbanID
- * @param int $regionID
- * @access public
- * @return void
- */
- public function getKanbanCallback($kanbanID, $regionID)
- {
- $kanbanData = $this->getKanbanData($kanbanID, $regionID);
- $kanbanData = reset($kanbanData);
- return array('name' => 'updateKanbanRegion', 'params' => array('region' . $regionID, $kanbanData));
- }
- /**
- * 获取卡片所在的单元格。
- * Get card cell.
- *
- * @param int $cardID
- * @param int $kanbanID
- * @access public
- * @return object
- */
- public function getCellByCard($cardID, $kanbanID)
- {
- return $this->dao->select('id,cards,lane,`column`')->from(TABLE_KANBANCELL)
- ->where('kanban')->eq($kanbanID)
- ->andWhere('type')->eq('common')
- ->andWhere('cards')->like("%,$cardID,%")
- ->fetch();
- }
- }
|